1 条题解
-
0
自动搬运
来自洛谷,原作者为

C1942huangjiaxu
我将终究顺流入大海搬运于
2025-08-24 21:38:18,当前版本为作者最后更新于2024-04-12 22:05:53,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
连通性问题自然考虑插头 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); }
- 1
信息
- ID
- 1530
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者