Codechef Sad Pairs——圆方树+虚树+树上差分

SADPAIRS

删点不连通,点双,圆方树 

非割点:没有影响

割点:子树DP一下

有不同颜色,所以建立虚树

在圆方树上dfs时候

如果当前点是割点

1.统计当前颜色虚树上的不连通点对,树形DP即可

2.统计所有颜色的虚树上的不连通点对。。。。

一个麻烦事是,虚树上一条边上选择一个原树割点,都会对这个虚树造成相同的影响(两边sz乘积)

n,m 2e5

树上差分

设虚树上,(x,y)的边,x是y的父亲

原树上,x的位置减去贡献,y的原树father位置加上贡献

最后dfs扫一遍就行了。

实际上麻烦事挺多:

1.不保证连通,所以圆方树森林,虚树森林

2.LCA可能是多个颜色虚树的节点,dp[x]是累计的

3.x断线只有的ans:原来本身不连通的+经过x的+x和同种别的颜色 (后面两个都是自己连通块内部)

错点:

1.dfn的cmp没有写进sort函数

2.vis在dfs时候没有赋值导致循环多遍

3.存在孤立点,不在任意一个DCC中,赋值typ=1的时候,特殊考虑到(其实typ=1没有意义)

4.统计的是当前连通块当前颜色的数量,所以初值:sz[x]=(co[x]==now)

5.答案爆int。两块sz相乘也会爆int

代码:

#include<bits/stdc++.h>
#define il inline
#define reg register int
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
    char ch;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
namespace Miracle{
const int N=4e5+5;
int n,m;
int a[N][2];
int kind;//species
struct node{
    int nxt,to;
}e[2*N];
int hd[N],cnt;
void add(int x,int y){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
    hd[x]=cnt;
}
int dfn[N],low[N],df,dfn2[N];
int co[N],b[N],dcc;
vector<int>mem[N];
ll preno;
vector<int>be[N];
int typ[N];//1:yuan 0:fang
int sta[N],top;
void tarjan(int x){
    dfn[x]=low[x]=++df;
    sta[++top]=x;
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(!dfn[y]){
            tarjan(y);
            low[x]=min(low[x],low[y]);
            if(low[y]>=dfn[x]){
                ++dcc;
                int z;
                do{
                    z=sta[top];
                    mem[dcc].push_back(z);
                    --top;
                }while(z!=y);
                mem[dcc].push_back(x);
            }
        }else low[x]=min(low[x],dfn[y]);
    }
}
int belong[N];
int dep[N],fa[N][20];
void dfs1(int x,int bl,int d){
    belong[x]=bl;
    dfn[x]=++df;
    dep[x]=d;
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(!dfn[y]){
            fa[y][0]=x;
            dfs1(y,bl,d+1);
        }
    }
    dfn2[x]=df;
}
ll dp[N];
ll tag[N];
int vis[N];
ll sz[N];
void finsz(int x,int now){
    vis[x]=now+kind;
    sz[x]=(co[x]==now);
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        finsz(y,now);
        sz[x]+=sz[y];
    }
}
void dfs2(int x,ll totsz,int now){
//    cout<<" dfs2 "<<x<<" : "<<totsz<<" "<<now<<endl;
    ll presz=0;
    //dp[x]=0;
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        dfs2(y,totsz,now);
        tag[x]-=(totsz-sz[y])*sz[y];
        //cout<<" fa[y][0] "<<fa[y][0]<<endl;
        tag[fa[y][0]]+=(totsz-sz[y])*sz[y];
        dp[x]+=presz*sz[y];
        presz+=sz[y];
    }
    dp[x]+=presz*(totsz-sz[x]);
    if(co[x]==now)dp[x]+=totsz-typ[x];
}
ll ans[N];

void sol(int x,int fa){
    vis[x]=1;
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa) continue;
        sol(y,x);
        tag[x]+=tag[y];
    }
    //cout<<" x "<<x<<" : "<<preno+dp[x]+tag[x]<<endl;
    if(typ[x]) ans[x]=preno+dp[x]+tag[x];
}
bool cmp(int x,int y){
    return dfn[x]<dfn[y];
}
int lca(int x,int y){
    if(dep[x]<dep[y]) swap(x,y);
    for(reg j=19;j>=0;--j){
        if(dep[fa[x][j]]>=dep[y]) x=fa[x][j];
    }
    if(x==y) return x;
    for(reg j=19;j>=0;--j){
        if(fa[x][j]!=fa[y][j]) x=fa[x][j],y=fa[y][j];
    }
    return fa[x][0];
}
int main(){
    rd(n);rd(m);
    int tot=0;
    for(reg i=1;i<=n;++i) rd(co[i]),b[++tot]=co[i];
    sort(b+1,b+tot+1);
    tot=unique(b+1,b+tot+1)-b-1;
    kind=tot;
    for(reg i=1;i<=n;++i){
        typ[i]=1;
        
        co[i]=lower_bound(b+1,b+tot+1,co[i])-b;
        //cout<<co[i]<<" ";
        be[co[i]].push_back(i);
    }
   // cout<<endl;
    
    int x,y;
    for(reg i=1;i<=m;++i){
        rd(x);rd(y);add(x,y);add(y,x);
    }
    
    for(reg i=1;i<=n;++i){
        if(!dfn[i]) tarjan(i);
    }
    memset(hd,0,sizeof hd);
    memset(dfn,0,sizeof dfn);
    df=0;cnt=0;
    tot=n;
    int edge=0;
    //cout<<" dcc "<<dcc<<endl;
    for(reg i=1;i<=dcc;++i){
        ++tot;
        typ[tot]=0;
        //cout<<"num ******************"<<tot<<endl;
        for(reg j=0;j<(int)mem[i].size();++j){
        //    cout<<mem[i][j]<<" ";
            typ[mem[i][j]]=1;
            a[++edge][0]=tot;
            a[edge][1]=mem[i][j];
            add(tot,mem[i][j]);
            add(mem[i][j],tot);
        }
        //cout<<endl;
    }
   // cout<<" tot "<<tot<<endl;
    int kuai=0;
    for(reg i=1;i<=tot;++i){
        if(!dfn[i]){
            dfs1(i,++kuai,1);
        }
    }
   // cout<<" kuai "<<kuai<<endl;
    memset(hd,0,sizeof hd);
    cnt=0;
    
    for(reg j=1;j<=19;++j){
        for(reg i=1;i<=n;++i){
            fa[i][j]=fa[fa[i][j-1]][j-1];
        }
    }
    for(reg i=1;i<=kind;++i){
        //cout<<" kind i "<<i<<" ------------------------------- "<<endl;
        int num=be[i].size();
        sort(be[i].begin(),be[i].end(),cmp);
        for(reg j=1;j<num;++j){
            vis[be[i][j]]=vis[be[i][j-1]]=i;
            if(belong[be[i][j]]==belong[be[i][j-1]]){
                int anc=lca(be[i][j],be[i][j-1]);
                if(vis[anc]!=i){
                        vis[anc]=i;
                    be[i].push_back(anc);
                }
            }
        }
        sort(be[i].begin(),be[i].end(),cmp);
        top=0;
        
        for(reg j=0;j<(int)be[i].size();++j){
        //    cout<<" j "<<be[i][j]<<endl;
            while(top&&(!(dfn[sta[top]]<=dfn[be[i][j]]&&dfn[be[i][j]]<=dfn2[sta[top]])))--top;
            if(top) add(sta[top],be[i][j]);
            sta[++top]=be[i][j];
        }
        
        ll has=0;
        for(reg j=0;j<(int)be[i].size();++j){
            if(vis[be[i][j]]!=i+kind){
                finsz(be[i][j],i);
            //    cout<<" totsz "<<be[i][j]<<" "<<typ[be[i][j]]<<" "<<sz[be[i][j]]<<endl;
                dfs2(be[i][j],sz[be[i][j]],i);
                preno+=has*sz[be[i][j]];
                has+=sz[be[i][j]];
            }
        }
        
        for(reg j=0;j<(int)be[i].size();++j){
            hd[be[i][j]]=0;
            sz[be[i][j]]=0;
        }
        cnt=0;
    }
//    cout<<" preno "<<preno<<endl;
//    for(reg i=1;i<=tot;++i){
//        printf("%d dp %lld %lld
",i,dp[i],tag[i]);
//    }
    memset(vis,0,sizeof vis);
    for(reg i=1;i<=edge;++i){
        add(a[i][0],a[i][1]);
        add(a[i][1],a[i][0]);
    }
    for(reg i=1;i<=tot;++i){
        if(!vis[i]){
            sol(i,0);
        }
    }
    for(reg i=1;i<=n;++i){
        printf("%lld
",ans[i]);
    }
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2019/2/19 17:48:21
*/
原文地址:https://www.cnblogs.com/Miracevin/p/10405198.html