[POI2008]BLO-Blockade

P3469 [POI2008]BLO-Blockade

题意:

有一个连通的有向图,求出删除一个点后,不能连通的点对的个数 (\((x,y),(y,x)\) 算两对)

分析:

很明显,既然涉及到环类求点集的题目,很明显是 \(tarjan\)

有一个性质:

从一个点集到另一个点集,形成的点对个数(相反算两对) 是:\(2 \times size[x]*size[y]\), 其中 \(size\) 是这个点集的大小

我们进行分类讨论:

\(x\) 不是割点:

断了这个点,对其他点的连通性貌似也没有影响,只是少了这个点关于其他点不能联通的点对。

因此答案就是: \(ans[x]=2 \times (n-1)\)

\(x\) 是割点:

断了这个点,其他一些点也要断了属于是。

我们画出来一张图表现一下:

我们选择断了 \(x\) 点,那么 \(y,z\)\(x\) 上方的节点也要断。

我们枚举 \(x\) 的子树 \(y,z\),分别计算这个集合到其他点的点对,不算其他点到这个集合的点对,避免重复。

从图分析则是:计算 \(y \rightarrow (x,z,father[x])\)\(z \rightarrow (x,y,father[x])\) 的点对,因此,则有转移方程:

ans[x]+=size[y]*(n-size[y])

我们发现,这样计算还有些纰漏。我们没有计算从父亲到 \(x\) 子树和 \(x\) 本身失去的点对个数。

我们计算 \(sum=size[y]+size[z]+...\)\(x\) 的子树大小(不包括 \(x\) ),那么 \(n-sum-1\) 则是父亲大小。

最后,我们还要计算 \(x\) 到其他点形成的点对 \(n-1\)

因此,最后加上:

ans[x]+=(sum+1)*(n-sum-1)+n-1;

最后输出即可。

代码:

//P3469 [POI2008]BLO-Blockade
#include<bits/stdc++.h>
using namespace std;
#define ll long long 
const int N=1e6+5;
int nxt[N],ver[N],tot,head[N];
int n,m,dfn[N],low[N],cnt,sta[N],top,sd[N],idx;
int sizes[N],cut[N];
ll ans[N];
bool vis[N];
void add(int x,int y){
    ver[++tot]=y;nxt[tot]=head[x];head[x]=tot;
}
void tarjan(int x){
    dfn[x]=low[x]=++cnt; 
    sizes[x]=1;vis[x]=1;
    int flag=0,sum=0;
    for(int i=head[x];i;i=nxt[i]){
        int y=ver[i]; 
        if(!dfn[y]){
            tarjan(y); sizes[x]+=sizes[y]; low[x]=min(low[x],low[y]);
            if(low[y]>=dfn[x]){
                ans[x]+=(ll)sizes[y]*(n-sizes[y]); sum+=sizes[y];
                flag++;
                if(x!=1||flag>1) cut[x]=true;
            }
        } 
        else if(vis[y]) low[x]=min(low[x],dfn[y]);
    }
    if(!cut[x]) ans[x]=2*(n-1);
    else ans[x]+=(ll)(n-sum-1)*(sum+1)+n-1;
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int x,y; scanf("%d%d",&x,&y); add(x,y);add(y,x);
    }
    tarjan(1);
    for(int i=1;i<=n;i++) cout<<ans[i]<<endl;
    system("pause");
    return 0;
}
posted @ 2021-10-04 21:21  Evitagen  阅读(24)  评论(0编辑  收藏  举报