题解:P2492 [SDOI2011] 火星移民
C1942huangjiaxu · · 题解
连通性问题自然考虑插头 DP。
先有个简单的想法,按照输入顺序依次加入三角形,每次记录还有边没被考虑过的三角形的连通情况,最多
可以用
考虑到我们记录了所有树的状态,但实际上我们只要记录链的状态。
考虑记录每条链的端点,链中间的点用
注意到最优解起点和终点会恰好连
发现考虑到第
所以我们可以在之前的状态中,单独记录
保证了之后状态数就不多了,可以通过。
上述方法仅供参考,可能存在更简洁的表示方法。
参考代码加了一些可有可无的剪枝,可读性可能较差。
参考代码(主体部分):
typedef unsigned long long ull;
typedef pair<int,int> pii;
const int N=225,P=1e9+7,Hs=3e6+17,M=1e6+5;
int n,m,a[N],ln[25],vis[4],mx[N],nw,ls,co[N],fa[25],nc[25],sz[25],l1,l2,e[N],E[N];
vector<int>p[N];
pii w[M][2];
int hd[Hs][2],num[2],nx[M][2];
ull to[M][2];
bool vs[25],lc[N],g[N];
inline int md(int x){
return x>=P?x-P:x;
}
inline pii operator +(pii a,pii b){
pii c;
c.first=min(a.first,b.first),c.second=0;
if(a.first==c.first)c.second=md(c.second+a.second);
if(b.first==c.first)c.second=md(c.second+b.second);
return c;
}
void add(int x,int y){
if(a[x]==4||a[y]==4)return;
if(x!=y+1)e[x]=y,E[y]=x;
else g[x]=true;
mx[y]=max(mx[y],x);
}
inline void ins(ull S,pii v){
int o=S%Hs;
for(int i=hd[o][nw];i;i=nx[i][nw])if(to[i][nw]==S){
w[i][nw]=w[i][nw]+v;
return;
}
nx[++num[nw]][nw]=hd[o][nw],hd[o][nw]=num[nw];
to[num[nw]][nw]=S,w[num[nw]][nw]=v;
}
inline void chg(int id,ull S,pii v,int o,int e0=0,int e1=0){
S>>=1;
v.first+=(e0>0)+(e1>0);
for(int i=1;i<=12;++i)fa[i]=i,vs[i]=false,sz[i]=0;
for(int i=0;i<l1;++i)co[p[id-1][i]]=S>>(4*i)&15,++sz[co[p[id-1][i]]];
if(e[id]&&co[e[id]]&&!lc[e[id]]&&e0!=e[id]&&e1!=e[id])return;
int &c=co[id];
c=12;
if(a[id]&&!vis[a[id]])c=a[id];
++sz[c];
if(!a[id]&&!o&&!e0)c=0;
if(e0&&!e1){
int &u=co[e0];
if(!u)return;
if(c<4){
fa[u]=c,c=0;
if(sz[u]>1)u=0;
}else{
c=u;
if(u<4||sz[u]>1)u=0;
}
}else if(e1){
if(co[e0]>co[e1])swap(e0,e1);
int &x=co[e0],&y=co[e1];
co[id]=0;
if(!x||x==y)return;
if(y<4)return;
fa[y]=x;
if(x<4){
x=0;
if(sz[y]>1)y=0;
}else{
if(sz[x]>1)x=0;
if(sz[y]>1)y=0;
}
}
for(int i=0;i<l2;++i){
int t=p[id][i],c=co[t];
if(!lc[t])continue;
if(!c)return;
if(fa[c]<4&&fa[c]!=a[t])return;
if(mx[t]<=id&&fa[c]!=a[t]){
bool fg=false;
if(id==m)return;
for(auto z:p[id])if(!lc[z]&&fa[co[z]]==fa[co[t]]){
fg=true;
break;
}
if(!fg)return;
}
}
ull T=0;
for(int i=0,ct=4;i<l2;++i){
int t=p[id][i];
if(!co[t])continue;
int u=fa[co[t]];
if(u<4){
T|=ull(u)<<(i*4);
vs[u]=true;
continue;
}
if(!vs[u])vs[u]=true,nc[u]=ct++;
T|=ull(nc[u])<<(i*4);
}
for(int i=1;i<4;++i)if(vis[i]==1&&!vs[i])return;
ins(T<<1|o,v);
}
void solve(){
m=6*n*n,num[0]=num[1]=0;
memset(vis,false,sizeof(vis));
memset(hd,0,sizeof(hd));
for(int i=1;i<=m;++i)e[i]=g[i]=E[i]=mx[i]=lc[i]=0;
for(int i=1;i<=2*n;++i){
if(i<=n)ln[i]=2*n+2*i-1;
else ln[i]=2*n+2*(2*n-i)+1;
}
for(int i=1,id=0;i<=2*n;++i){
for(int j=1;j<=ln[i];++j){
++id;
scanf("%d",&a[id]);
if(a[id]&&a[id]!=4){
if(vis[a[id]])lc[id]=true;
vis[a[id]]=true;
}
if(j>1)add(id,id-1);
if((i<=n)!=(j&1)){
if(i>1){
if(i<=n)add(id,id-ln[i]+1);
if(i==n+1)add(id,id-ln[i]);
if(i>n+1)add(id,id-ln[i]-1);
}
}
}
}
for(int i=0;i<=m;++i){
p[i].clear();
for(int j=1;j<=i;++j)if(mx[j]>i||lc[j])p[i].push_back(j);
}
memset(vis,false,sizeof(vis));
nw=0,ls=1;
ins(0,{0,1});
for(int i=1;i<=m;++i){
l1=p[i-1].size(),l2=p[i].size();
swap(nw,ls),num[nw]=0;
if(a[i]==4)a[i]=0;
for(int j=1;j<=num[ls];++j){
ull S=to[j][ls];pii v=w[j][ls];
hd[S%Hs][ls]=0;
if(!(S&1)){
if(a[i]){
if(g[i+1])chg(i,S,v,1);
if(e[i])chg(i,S,v,0,e[i]);
if(E[i])chg(i,S,v,0);
}else{
chg(i,S,v,0);
if(g[i+1]&&e[i])chg(i,S,v,1,e[i]);
if(g[i+1]&&E[i])chg(i,S,v,1);
}
}else{
if(a[i]){
chg(i,S,v,0,i-1);
}else{
if(g[i+1])chg(i,S,v,1,i-1);
if(e[i])chg(i,S,v,0,i-1,e[i]);
if(E[i])chg(i,S,v,0,i-1);
}
}
}
++vis[a[i]];
}
if(!num[nw]){
puts("-1 -1");
return;
}
pii ans={100000,0};
for(int i=1;i<=num[nw];++i)ans=ans+w[i][nw];
printf("%d %d\n",ans.first,ans.second);
}