BZOJ 1123: [POI2008]BLO 求割点_乘法原理_计数
一段巧妙的代码:
if(low[to[v]]>=pre[u]) {
answer[u]+=(ll)t*siz[to[v]];
t+=siz[to[v]];
}
考虑要实现每两个联通块之间分别乘一次,并加和.
用这种方式可以实现每两个联通块之间只乘一次.
Code:
#include<bits/stdc++.h>
#define setIO(s)freopen(s".in","r",stdin)
#define maxn 1100000
using namespace std;
int n;
#define ll long long
long long answer[maxn];
int cnt=0,scc=0;
int nex[maxn],to[maxn],head[maxn];
void add(int u,int v){ nex[++cnt]=head[u],head[u]=cnt,to[cnt]=v;}
int low[maxn],pre[maxn];
ll siz[maxn];
void dfs(int u,int fa){
low[u]=pre[u]=++scc;
siz[u]=1;
ll t=0;
for(int v=head[u];v;v=nex[v]){
if(to[v]==fa) continue;
if(pre[to[v]]) low[u]=min(low[u],pre[to[v]]);
else {
dfs(to[v],u);
siz[u]+=siz[to[v]];
if(low[to[v]]>=pre[u]) {
answer[u]+=(ll)t*siz[to[v]];
t+=siz[to[v]];
}
low[u]=min(low[u],low[to[v]]);
}
}
answer[u]+=(ll)((ll)n-t-1)*t;
}
int main()
{
//setIO("input");
int m;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
dfs(1,0);
for(int i=1;i<=n;i++) printf("%lld\n",(ll)(answer[i]+(ll)n-1)*2);
return 0;
}