UPC Decayed Bridges(并查集+思维)
知足且坚定 温柔且上进
Decayed Bridges
题目描述
There are N islands and M bridges.
The i-th bridge connects the Ai-th and Bi-th islands bidirectionally.
Initially, we can travel between any two islands using some of these bridges.
However, the results of a survey show that these bridges will all collapse because of aging, in the order from the first bridge to the M-th bridge.
Let the inconvenience be the number of pairs of islands (a,b) (a<b) such that we are no longer able to travel between the a-th and b-th islands using some of the bridges remaining.
For each i (1≤i≤M), find the inconvenience just after the i-th bridge collapses.
Constraints
All values in input are integers.
·2≤N≤105
·1≤M≤105
·1≤Ai<Bi≤N
·All pairs (Ai,Bi) are distinct.
·The inconvenience is initially 0.
输入
Input is given from Standard Input in the following format:
N M
A1 B1
A2 B2
⋮
AM BM
输出
In the order i=1,2,…,M, print the inconvenience just after the i-th bridge collapses. Note that the answer may not fit into a 32-bit integer type.
样例输入 Copy
4 5
1 2
3 4
1 3
2 3
1 4
样例输出 Copy
0
0
4
5
6
提示
For example, when the first to third bridges have collapsed, the inconvenience is 4 since we can no longer travel between the pairs (1,2),(1,3),(2,4) and (3,4).
题意: 起初给你一张无向图,然后按照给定的顺序删边,问每删一次边会造成多少损失(损失即不联通的两个点)
思路: 正着写是不好写的,我们不妨反过来考虑。假设一开始所有点都是孤立的,我把删边看成是添边,倒着加边,然后用并查集维护一下联通块的个数,就很轻松推出来了。
最开始所有点都是孤立的,损失的总数为n*(n-1)/2。因为首先从n个点里选1个点,有n种选法,再从剩下的n-1个点里选1个点,有n-1种选法,根据乘法原理,是n*(n-1).那么又因为(a,b)和(b,a)是一样的,所以要/2以去重。
我们每加一条边,都更新一下损失和各联通块内点的数量,每次损失增加的量即是cnt[x]*cnt[y]原理跟上面类似的~
不会并查集维护联通块的先看这个题~837. 连通块中点的数量 - AcWing题库
837代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
//cnt[]表示祖宗节点所在集合中点的个数
int root[N],cnt[N];
int ffind(int x){
return root[x] == x ? x : root[x] = ffind(root[x]);
}
void uunion(int a,int b){
int x = ffind(a),y = ffind(b);
if(x != y){
cnt[y] += cnt[x];
root[x] = y;
}
}
int main(){
int n,m;
cin>>n>>m;
for(int i = 1; i <= n; i++) root[i] = i, cnt[i] = 1;
while(m--){
char oper[2];
int x,y;
scanf("%s",oper);
if(oper[0] == 'C'){
cin>>x>>y;
uunion(x,y);
}
else if(oper[1] == '1'){
cin>>x>>y;
if(ffind(x) == ffind(y)) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
else{
cin>>x;
cout<<cnt[ffind(x)]<<endl;
}
}
return 0;
}
本题代码:
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define I_int ll
inline ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
char F[200];
inline void out(I_int x) {
if (x == 0) return (void) (putchar('0'));
I_int tmp = x > 0 ? x : -x;
if (x < 0) putchar('-');
int cnt = 0;
while (tmp > 0) {
F[cnt++] = tmp % 10 + '0';
tmp /= 10;
}
while (cnt > 0) putchar(F[--cnt]);
//cout<<" ";
}
const int maxn=1e6+7;
ll n,m;
ll a[maxn],b[maxn];
ll root[maxn],cnt[maxn],tot[maxn];
ll res,ans=0;
ll Find(ll x){
if(x==root[x]) return root[x];
else return root[x]=Find(root[x]);
}
void Union(ll a,ll b){
ll x=Find(a),y=Find(b);
if(x!=y){
ans+=cnt[x]*cnt[y];
cnt[x]+=cnt[y];
root[y]=x;
}
}
void init(){
for(ll i=1;i<=n;i++) root[i]=i,cnt[i]=1;///并查集
}
void AC(){
n=read();m=read();
for(ll i=1;i<=m;i++){
a[i]=read();
b[i]=read();
}
init();
res=n*(n-1)/2;
for(ll i=m;i;i--){
tot[i]=res-ans;
Union(a[i],b[i]);
//cout<<i<<" "<<ans<<endl;
}
for(ll i=1;i<=m;i++) printf("%lld\n",tot[i]);
}
int main(){
AC();
return 0;
}
这个题的逆向思维还是很好的~