UOJ Logo 3002xz的博客

博客

ZJOI2020 染色游戏 题解

2021-03-19 13:26:32 By 3002xz

先考虑 $m=n-1$ 的部分如何处理。

$f_i$ 表示 Alice 可以用 $1$ 步占据 $i$ 号点,在 $i$ 号点子树内进行游戏的答案。

我们用 $g_i$ 表示 $i$ 号点子树内距离 $i$ 号点最近的被 Bob 占据的距离

那么如果 $i \in T$,显然 $f_i=g_i$

否则 $f_i=\min(g_i,\sum_{j \in son_i} \max(f_j-\operatorname{dis(i,j)},0))$

如果环上存在 Bob 占据的节点,显然该节点在环上往 $2$ 侧的拓展是独立的,我们将其分裂成 $2$ 个虚点,分别连接相邻的 $2$ 侧节点即可。

考虑更一般的情况,假设 $1$ 号节点在环上,我们分如下几种情况考虑。

  1. 将环提为根,分裂成基环大小棵树,不存在跨树覆盖的情况,那么此时我们只要算出每棵树的答案,保证顺逆时针答案的前缀和均 $\geq 0$ (即不管Alice从哪个方向覆盖都能即时转移),只要顺逆时针分别进行一次贪心,当前缀和 $<0$ 时强行修正,易证这个贪心时正确的。
  2. 存在跨树覆盖的情况,那么此时我们会发现,如果有超过 $2$ 棵树进行跨树覆盖,显然是不合法的,如果存在一棵树跨树覆盖,我们可以枚举其覆盖到环上的左右端点,那么此时因为该节点在环上,整个环被割裂成 $2$ 部分,分别求 $2$ 侧答案进行求和即可,注意此时 Bob 从子树内出来的步骤是公共的,需要特判一下,如果有 $2$ 棵树进行跨树覆盖,我们枚举这 $2$ 个节点,并枚举其覆盖到的左右端点,分别算答案相加即可。

如果 $1$ 号点不在环上,我们可以将整个环部分的答案先处理出来,当作一个虚点挂在 $1$ 号点所在的子树上,注意此时可能会有环上节点覆盖到 $1$ 号点子树内的情况,再挂一个虚点,修改其$g_i$ 即可。

//zxggtxdy!!!
#include<bits/stdc++.h>
using namespace std;
const int N=1007; bool tag,f1[N],f2[N],fl[N],gt[N];
int n,m,s,t,op,rt,og,st[N],pt[N],f[N],G[N],fa[N],ft[N],Gt[N],dis[N][N]; long long res,V,Gs,F[N],D[N],va[N],vb[N],vc[N],vd[N],ch[N][2]; vector<int>v[N],w[N],vt[N];
struct edg{
    int a,b,c;
}e[N];
inline int find(int u){
    if(f[u]==u) return u; return f[u]=find(f[u]);
}
inline void chk(int u){
    if(u==1) tag=1;
    for(int i=0;i<v[u].size();i++){
        int x=v[u][i]; if(fa[u]==x) continue; fa[x]=u,chk(x);
    }
}
inline void dfs(int u,int dep){
    F[u]=0; if(u==rt) F[u]=V,D[u]=Gs;
    for(int i=0;i<v[u].size();i++){
        int x=v[u][i]; if(fa[u]==x) continue;
        ft[x]=w[u][i],fa[x]=u,dfs(x,dep+w[u][i]+1);
        if(D[x]!=-1){
            D[x]+=w[u][i]+1; if(D[u]==-1) D[u]=D[x]; else D[u]=min(D[u],D[x]);
        }
        F[u]+=max(0ll,F[x]-1-w[u][i]);
    }
    if(f1[u]) D[u]=0,F[u]=0;
    else{
        if(f2[u]){
            if(D[u]!=-1) F[u]=D[u]+1; else F[u]=1e15;
        }
        else if(u!=1){
            if(D[u]!=-1&&F[u]>=D[u]+1) F[u]=D[u]+1;
        }
    }
    if(u==1&&!f2[1]){
        res=min(res,F[1]);
    }
}
inline void bfs(int x,int y,int fa){
    st[++st[0]]=x;
    if(x==y){
        for(int i=1;i<=st[0];i++) fl[st[i]]=1,Gt[i]=st[i];
    }
    for(int i=0;i<v[x].size();i++){
        int c=v[x][i]; if(fa==c) continue; bfs(c,y,x);
    }
    st[0]--;
}
int main(){
    int C,a,b,c; cin>>C; 
    while(C--){
        rt=0;
        scanf("%d%d%d%d",&n,&m,&s,&t); op++; bool tg=0;
        for(int i=1;i<=n;i++) f[i]=i,fl[i]=0; int x,y;
        for(int i=1;i<=n;i++) v[i].resize(0),w[i].resize(0),f1[i]=0,f2[i]=0,D[i]=-1;
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&a,&b,&c);
            e[i].a=a,e[i].b=b,e[i].c=c;
            if(find(a)==find(b)){
                x=a,y=b; continue;
            }
            v[a].push_back(b),v[b].push_back(a);
            w[a].push_back(c),w[b].push_back(c);
            f[find(a)]=find(b);
        }
        for(int i=1;i<=s;i++){
            scanf("%d",&a),f1[a]=1;
        }
        for(int i=1;i<=t;i++){
            scanf("%d",&a),f2[a]=1;
        }
        if(f2[1]){
            puts("1000000"); continue;
        }
        if(n==m){
            st[0]=0,bfs(x,y,0);
            for(int i=1;i<=n*2;i++) v[i].resize(0),w[i].resize(0);
            for(int i=n+1;i<=n*2;i++) f1[i]=f2[i]=0;
            for(int i=1;i<=n;i++) if(f1[i]&&fl[i]) x=i,tg=1;
            for(int i=1;i<=n;i++) if(fl[i]) st[++st[0]]=i;
            for(int i=1;i<=m;i++){
                a=e[i].a,b=e[i].b,c=e[i].c;
                if(tg){
                    if(a==x) a=++n,f1[a]=1;
                    else if(b==x) b=++n,f1[b]=1;
                }
                v[a].push_back(b),v[b].push_back(a);
                w[a].push_back(c),w[b].push_back(c);
            }
        }

        for(int i=1;i<=n;i++) if(f1[i]) f2[i]=0;
        res=1000000;
        if(m==n-1||tg){
            dfs(1,0);
            printf("%lld\n",res);
        }
        else{
            res=1000000,V=1e15; Gs=1e15;
            for(int i=1;i<=n;i++) gt[i]=0,fa[i]=0,F[i]=0;
            for(int i=1;i<=st[0];i++) gt[st[i]]=1,st[i]=Gt[i];
            for(int i=1;i<=n;i++) v[i].resize(0),w[i].resize(0);
            for(int i=1;i<=m;i++){
                a=e[i].a,b=e[i].b,c=e[i].c,dis[a][b]=dis[b][a]=c+1;
                if(gt[a]&&gt[b]){
                    continue;
                }
                v[a].push_back(b),v[b].push_back(a);
                w[a].push_back(c),w[b].push_back(c);
            }
            for(int i=1;i<=st[0];i++){
                tag=0,chk(st[i]);
                if(!tag) continue; int L=0;
                for(int j=i;j<=st[0];j++) pt[++L]=st[j];
                for(int j=1;j<i;j++) pt[++L]=st[j];
                for(int j=1;j<=L;j++) st[j]=pt[j]; break;
            }
            st[st[0]+1]=st[1];
            for(int i=2;i<=st[0];i++){
                dfs(st[i],0),ch[i][0]=ch[i][1]=1e15,og=0;
                if(D[st[i]]==-1) continue; long long Q=1,E=1;
                for(int j=1;j<i;j++) Q+=dis[st[j]][st[j+1]];
                for(int j=i;j<=st[0];j++) E+=dis[st[j]][st[j+1]];
                if(st[1]!=1) V=min(V,min(Q,E)+D[st[i]]);//block up the circle
                if(st[1]!=1) Gs=min(Gs,min(Q,E)+D[st[i]]);
            }
            Gs--;
            va[1]=0,vb[1]=0;
            for(int i=2;i<=st[0];i++){
                va[i]=va[i-1],vb[i]=vb[i-1]+dis[st[i-1]][st[i]]-f2[st[i]]; long long D=F[st[i]];
                if(D-vb[i]>0) va[i]+=D-vb[i],vb[i]=0; else vb[i]-=D; vb[i]+=f2[st[i]];
            }
            vc[st[0]+1]=vd[st[0]+1]=0;

            for(int i=st[0];i>=2;i--){
                vc[i]=vc[i+1],vd[i]=vd[i+1]+dis[st[i]][st[i+1]]-f2[st[i]]; long long D=F[st[i]];
                if(D-vd[i]>0) vc[i]+=D-vd[i],vd[i]=0; else vd[i]-=D; vd[i]+=f2[st[i]];
            }
            for(int i=2;i<=st[0];i++){
                if(D[st[i]]==-1) continue;
                long long A=0,B=0,T1=0,T2=0;
                for(int j=1;j<=i-1;j++) T1+=dis[st[j]][st[j+1]];
                for(int j=i+1;j<=st[0]+1;j++) T2+=dis[st[j-1]][st[j]];
                A=0,B=0;

                for(int j=i-1;j>=1;j--){
                    B=0;
                    for(int k=i+1;k<=st[0]+1;k++){
                        long long C=va[j]+vc[k];
                        long long X=vb[j]+dis[st[j]][st[j+1]]-1;
                        long long Y=vd[k]+dis[st[k]][st[k-1]]-1;
                        if(A-X<=-D[st[i]]&&B-Y<=-D[st[i]]){
                            //do nothing
                        }
                        else{
                            C+=D[st[i]]+min(max(A-X,B-Y),0ll);
                            C+=max(0ll,A-X)+max(0ll,B-Y);
                        }
                        ch[i][0]=min(max(0ll,A-X+D[st[i]])+va[j],ch[i][0]);
                        ch[i][1]=min(max(0ll,B-Y+D[st[i]])+vc[k],ch[i][1]);
                        V=min(V,C);
                        B+=dis[st[k-1]][st[k]];
                    }
                    A+=dis[st[j+1]][st[j]];
                }
            }
            //choose two root
            for(int i=2;i<=st[0];i++)
                for(int j=i+1;j<=st[0];j++){
                    V=min(V,ch[i][0]+ch[j][1]);
                }
            //no Bob's operatr from other tree
            long long S=0,ans=0;
            for(int i=2;i<=st[0];i++){
                S+=dis[st[i-1]][st[i]]-f2[st[i]];
                if(F[st[i]]>=S) ans+=F[st[i]]-S,F[st[i]]=S,S=0;
                else S-=F[st[i]]; S+=f2[st[i]];
            }
            S=0;
            for(int i=st[0];i>=1;i--){
                S+=dis[st[i+1]][st[i]]-f2[st[i]];
                if(F[st[i]]>=S) ans+=F[st[i]]-S,F[st[i]]=S,S=0;
                else S-=F[st[i]]; S+=f2[st[i]];
            }
            V=min(V,ans);
            if(st[1]==1){
                fa[1]=0,dfs(1,0);
                printf("%lld\n",min(1000000ll,V+F[1]));
            }
            else{
                fa[1]=0,rt=st[1],dfs(1,0),rt=0,printf("%lld\n",res);
            }
        }
    }
}

评论

EntropyIncreaser
3002 个 xz nb!
xllend3
太棒了,学到虚脱
Rainbow_sjy
大棒子,学到虚脱
return20071007
太棒了,学到虚脱

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。