1. 程式人生 > >【洛谷P3469】[POI2008]LO-Blockade 解題報告

【洛谷P3469】[POI2008]LO-Blockade 解題報告

P3469[POI2008]LO-Blockade

題意翻譯

在Byteotia有n個城鎮。 一些城鎮之間由無向邊連線。 在城鎮外沒有十字路口,儘管可能有橋,隧道或者高架公路(反正不考慮這些)。每兩個城鎮之間至多隻有一條直接連線的道路。人們可以從任意一個城鎮直接或間接到達另一個城鎮。 每個城鎮都有一個公民,他們被孤獨所困擾。事實證明,每個公民都想拜訪其他所有公民一次(在主人所在的城鎮)。所以,一共會有n*(n-1)次拜訪。

不幸的是,一個程式設計師總罷工正在進行中,那些程式設計師迫切要求購買某個軟體。

作為抗議行動,程式設計師們計劃封鎖一些城鎮,阻止人們進入,離開或者路過那裡。

正如我們所說,他們正在討論選擇哪些城鎮會導致最嚴重的後果。

編寫一個程式:

讀入Byteotia的道路系統,對於每個被決定的城鎮,如果它被封鎖,有多少訪問不會發生,輸出結果。

輸入輸出格式

第一行讀入n,m,分別是城鎮數目和道路數目

城鎮編號1~n

接下來m行每行兩個數字a,b,表示a和b之間有有一條無向邊

輸出n行,每行一個數字,為第i個城鎮被鎖時不能發生的訪問的數量。

題目描述

There are exactly nn towns in Byteotia.

Some towns are connected by bidirectional roads.

There are no crossroads outside towns, though there may be bridges, tunnels and flyovers. Each pair of towns may be connected by at most one direct road. One can get from any town to any other-directly or indirectly.

Each town has exactly one citizen.

For that reason the citizens suffer from loneliness.

It turns out that each citizen would like to pay a visit to every other citizen (in his host's hometown), and do it exactly once. So exactly n\cdot (n-1)n⋅(n−1) visits should take place.

That's right, should.

Unfortunately, a general strike of programmers, who demand an emergency purchase of software, is under way.

As an act of protest, the programmers plan to block one town of Byteotia, preventing entering it, leaving it, and even passing through.

As we speak, they are debating which town to choose so that the consequences are most severe.

Task Write a programme that:

reads the Byteotian road system's description from the standard input, for each town determines, how many visits could take place if this town were not blocked by programmers, writes out the outcome to the standard output.

給定一張無向圖,求每個點被封鎖之後有多少個有序點對(x,y)(x!=y,1<=x,y<=n)滿足x無法到達y

輸入輸出格式

輸入格式:

In the first line of the standard input there are two positive integers: \(n\) and \(m\) (\(1\le n\le 100000, 1\le m\le 500000\) )denoting the number of towns and roads, respectively.

The towns are numbered from 1 to \(n\).

The following mm lines contain descriptions of the roads.

Each line contains two integers \(a\) and \(b\) (\(1\le a<b\le n\)) and denotes a direct road between towns numbered \(a\) and \(b\).

輸出格式:

Your programme should write out exactly nn integers to the standard output, one number per line. The i^{th}ithline should contain the number of visits that could not take place if the programmers blocked the town no. \(i\)

輸入輸出樣例

輸入樣例#1:

5 5
1 2
2 3
1 3
3 4
4 5

輸出樣例#1:

8
8
16
14
8

演算法

Tarjan找割點

思路

I.設在搜尋樹T中以x為根的樹包含的點集為SubTree(x)。

II.這裡去除割點可以理解為刪除與該點相連的所有邊。

III.這裡提到的連通塊是指當某一割點去除時:1.其中任何兩個點都能相互到達 2.沒有更大的連通塊包含該塊 當然,根據II,我們把單獨的X也看做一個連通塊

IV.為了方便,我們直接將(x,y)(滿足x不能到y)成為“點對”

V. ~S:S的補集,A^B:在A中不包括點B的所有點構成的集合

利用Tarjan查詢割點的同時,我們可以找出該割點X去除後剩餘的連通塊(有兩種情況,一種是在SubTree(X)^X中,另一種是~SubTree(X))。

只要能夠理解割點的求解過程,這還是很好理解的,這裡不再贅述。

然後要求“點對”。

對於一個點X,不管是否為割點,點對(i,j)為“點對”,當且僅當

i != j且i、j屬於兩個不同的連通塊

根據定義,很容易證明這個推論。

程式碼有多種寫法,這裡選取我能想到的最簡單的寫法。

在列舉連通塊時,ans加上s[to[i]] * ( n - s[to[i]] ),即一次性處理一個連通塊的所有點,它們與其他不屬於這個連通塊的點都構成“點對”。當然,別忘了X與~SubTree(X)。

程式碼

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define MAXN 100005
#define MAXM 1000005

int n, m;
int hd[MAXN], nxt[MAXM], to[MAXM], tot;
int dfn[MAXN], low[MAXN], root, num;
LL ans[MAXN];
int s[MAXN];

void Add( int x, int y ){ nxt[++tot] = hd[x]; hd[x] = tot; to[tot] = y; }

void DFS( int x ){
    s[x] = 1;
    low[x] = dfn[x] = ++num;
    LL b(0);
    for ( int i = hd[x]; i; i = nxt[i] ){
        if ( !dfn[to[i]] ){
            DFS( to[i] ); s[x] += s[to[i]];
            low[x] = min( low[x], low[to[i]] );
            if ( dfn[x] <= low[to[i]] ) ans[x] += (long long)s[to[i]] * ( n - s[to[i]] ), b += s[to[i]];//發現新的連通塊!
        } else low[x] = min( low[x], dfn[to[i]] );
    }
    ans[x] += (long long)( n - b - 1 ) * ( b + 1 ) + ( n - 1 );//算上~SubTree(X)與X
}

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 );
    }
    DFS( 1 );
    for ( int i = 1; i <= n; ++i ) printf( "%lld\n", ans[i] );
    return 0;
}