压力

来源:互联网 发布:g71循环编程实例 编辑:程序博客网 时间:2024/06/02 13:51

题目大意

有一张联通无向图,还有许多点对,你需要输出每个点是多少点对的必经点(即如果点对为u,v,那么如果u到v无论如何都要经过x,那么x是该点对的必经点)

建一颗富榄树

富爷:这个叫割点树,是我命名的
那么我们暂且称他为富榄树
富榄树是什么?我们清楚,如果是一张有向图,如果一提到必经点,我们脑海中可以跳出来一个东西——抖M树(dominator tree),它对每个节点保留了一个最近支配点,然后让每个点的父亲为其最近支配点建出了一棵树,这个棵树叫抖M树。假设根节点为1,那么对于任意节点i,只有1到i在抖M树的树路径上的点删去后会对1和i的连通性造成影响。
富榄树的思想类似,首先对无向图建出dfs树,然后每个点保留其最近割点。那你要问啦,无向图割点是怎么定义的?无向图的割点只有相对而言,x对于y是割点,则定义z为x的一个儿子且z为y祖先,那么如果low[z]>=dfn[x],则x是y的一个割点。每个点都可能有很多割点,只需要保留最近那个即可。
具体的,我们盗用一下抖M树的数组名,用idom[i]表示i的最近割点,那么j是i的父亲,如果low[i]>=dfn[j](即j是i的割点),idom[i]=j,否则idom[i]=idom[j](因为j的割点一定是i的割点)。然后求出了idom,我们就可以建出抖M富榄树啦!(鼓掌熊
与抖M树类似,富榄树更加强大的是,他不需要起点固定。

离线大法好

对于路线u到v,我们求出其在dfs树上的lca w。找到一个在dfs树上深度最小的x满足其在富榄树上是u的祖先且其在dfs树上深度大于w,同样对于v找到一个y。那么我们知道,在富榄树上,u到x的树路径上的所有点以及v到y的树路径上的所有点被删去后u和v的连通性将受到影响,所以这些点的ans得加一。w删去后会不会对u和v的连通性有影响呢?如果w是x或y其中一个的割点,则ans[w]++,否则显然删去w后从u出发到x然后跳到上面去再飞下来到y再走到v是合法的。
然后现在就变成若干操作每个操作形如把u到v路径上的点ans都加一。这个显然可以离线做,即我们弄一个cnt,在u时影响+1,到了v时要消除影响。
貌似要特判u和v在一条链上的情况,反正我特判了,详见代码。
有一个有趣的东西是,我们用tarjan求low和dfn顺便建dfs树,同时我们可以用tarjan求lca,%%%tarjan。

#include<cstdio>#include<algorithm>#include<cmath>#include<vector>#define fo(i,a,b) for(i=a;i<=b;i++)#define fd(i,a,b) for(i=a;i>=b;i--)using namespace std;const int maxn=100000+10,maxm=200000*2+10,maxp=200000+10;struct dong{    int u,v,w;};dong ask[maxp];int h4[maxn],h[maxn],d[maxn],go[maxm],next[maxm],dfn[maxn],nfd[maxn],low[maxn],fa[maxn],fath[maxn],fat[maxn][20],ans[maxn];int h1[maxm],n1[maxm],g1[maxm],h2[maxm],n2[maxm],g2[maxm],h3[maxm],n3[maxm],g3[maxm];bool bz[maxn],pp[maxm],ppp[maxn];int i,j,k,l,t,n,m,p,tot,t1,t2,t3,top,cnt,u,v,w,x,y,limit;void add(int x,int y){    go[++tot]=y;    next[tot]=h[x];    h[x]=tot;}void add1(int x,int y){    g1[++t1]=y;    n1[t1]=h1[x];    h1[x]=t1;}void add2(int x,int y){    g2[++t2]=y;    n2[t2]=h2[x];    h2[x]=t2;}void add3(int x,int y){    g3[++t3]=y;    n3[t3]=h3[x];    h3[x]=t3;}int getfa(int x){    return fa[x]?fa[x]=getfa(fa[x]):x;}void tarjan(int x){    low[x]=dfn[x]=++top;    nfd[top]=x;    int t=h[x],i,y,z;    while (t){        if (!dfn[go[t]]){            d[go[t]]=d[x]+1;            tarjan(go[t]);            fa[go[t]]=x;            fath[go[t]]=x;            low[x]=min(low[x],low[go[t]]);        }        else low[x]=min(low[x],dfn[go[t]]);        t=next[t];    }    t=h2[x];    while (t){        i=g2[t];        if (x==ask[i].u) swap(ask[i].u,ask[i].v);        y=ask[i].u;        if (bz[y]){            y=getfa(y);            ask[i].w=y;        }        t=n2[t];    }    bz[x]=1;}void solve(int x){    bz[x]=1;    int t=h[x];    while (t){        if (d[go[t]]==d[x]+1&&!bz[go[t]]){            if (low[go[t]]>=dfn[x]) fat[go[t]][0]=x;else fat[go[t]][0]=fat[x][0];            solve(go[t]);        }        t=next[t];    }}int dfs(int x){    int t,y,cnt=0;    t=h1[x];    while (t){        y=g1[t];        cnt+=dfs(y);        t=n1[t];    }    t=h3[x];    while (t){        y=g3[t];        cnt++;        h4[y]++;        t=n3[t];    }    ans[x]+=cnt;    cnt-=h4[x];    h4[x]=0;    return cnt;}int main(){    scanf("%d%d%d",&n,&m,&p);    fo(i,1,m){        scanf("%d%d",&j,&k);        add(j,k);        add(k,j);    }    fo(i,1,p){        scanf("%d%d",&j,&k);        ask[i].u=j;ask[i].v=k;        add2(j,i);        add2(k,i);    }    d[1]=1;    tarjan(1);    low[1]=0;    fo(i,1,n) bz[i]=0;    solve(1);    fo(i,2,n)        add1(fat[i][0],i);    fo(j,1,floor(log(n)/log(2)))        fo(i,1,n)            fat[i][j]=fat[fat[i][j-1]][j-1];    fo(i,1,p){        u=ask[i].u;v=ask[i].v;w=ask[i].w;        if (v==w) swap(u,v);        if (u==w){            y=v;            fd(j,floor(log(n)/log(2)),0)                if (d[fat[y][j]]>d[w]) y=fat[y][j];            add3(v,y);            ans[u]++;            continue;        }        x=u;        fd(j,floor(log(n)/log(2)),0)            if (d[fat[x][j]]>d[w]) x=fat[x][j];        y=v;        fd(j,floor(log(n)/log(2)),0)            if (d[fat[y][j]]>d[w]) y=fat[y][j];        if (fat[x][0]==w||fat[y][0]==w) ans[w]++;        add3(u,x);        add3(v,y);    }    t=dfs(1);    fo(i,1,n) printf("%d\n",ans[i]);}
0 0