洛谷P3469 [POI2008]BLO-Blockade 割點

題目鏈接:https://www.luogu.com.cn/problem/P3469

分兩種情況;
情況1:選擇的不是割點,說明刪除該點後,整張圖還是連通的,剩餘 n-1 個點還是可以互相訪問的,只是少了被刪的點訪問其他點與其他點訪問被刪的點,即(n-1)*2 次。
情況2:選擇的是割點,刪去該點後,整個圖會被分成 k 個連通塊,訪問此時會少Σki (第i個連通塊的節點數) 乘 n-ki+ n-1(別忘了被刪節點訪問其它節點的次數)。
我們用tarjan來判斷每個點是否是割點,並再tarjan的過程中,記以每個節點爲根的子樹大小。如果 low[u] >= dfn[v],說明u不能通過其它路徑到達v之前的點,所以v就是割點,當然如果 v 是根的話,只有他有兩個不能到達v之前的子樹,纔是割點,本題特判一下 點1 即可。
代碼如下

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int maxm=1e6+5;
typedef long long ll;
struct node
{
    int to,next;
}p[maxm];
int head[maxn],dfn[maxn],low[maxn];
ll ans[maxn];
int size[maxn];
int cnt,dfn_num;
int n,m;
bool cut[maxn];//標記是否爲割點
void add(int x,int y)
{
    p[++cnt].next=head[x];
    p[cnt].to=y;
    head[x]=cnt;
}
void tarjan(int u)
{
    dfn[u]=low[u]=++dfn_num;
    size[u]=1;//以該點爲根的子樹大小
    ll sum=0;
    int tag=0;//判斷是否是割點
    for(int i=head[u];i;i=p[i].next)
    {
        int v=p[i].to;
        if(!dfn[v])
        {
            tarjan(v);
            size[u]+=size[v];
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u])
            {
                sum+=size[v];
                ans[u]+=(ll)size[v]*((ll)n-(ll)size[v]);
                tag++;
                if(tag>1||u!=1)
                cut[u]=1;
            }
        }
        else
        low[u]=min(low[u],dfn[v]);
    }
    if(!cut[u])
    ans[u]=(n-1)*2;
    else
    ans[u]+=(ll)(n-sum-1)*(ll)(sum+1)+(n-1);
}
int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        add(x,y);
        add(y,x);
    }
    for(int i=1;i<=n;i++)
    if(!dfn[i])
    tarjan(i);
    for(int i=1;i<=n;i++)
    printf("%lld\n",ans[i]);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章