传送门:bzoj5463
题解
建出广义圆方树。
将圆点权值看做-1,方点权值看做所连接的圆点数量。
两个圆点分别作为
s
,
f
s,f
s,f点时
c
c
c点的方案数即它们在圆方树上简单路径的权值和。
转而枚举每个点被计算次数(经过路径数),时间复杂度 O ( n ) O(n) O(n)
代码
#include<bits/stdc++.h>
#define gc getchar
using namespace std;
const int N=2e5+10;
typedef long long ll;
int n,m,num,sz[N],val[N],S;
int df[N],low[N],stk[N],top,dfn;
ll ans;
char cp;
template<class T>inline void rd(T &x)
{
cp=gc();x=0;int f=0;
for(;!isdigit(cp);cp=gc()) if(cp=='-') f=1;
for(;isdigit(cp);cp=gc()) x=x*10+(cp^48);
if(f) x=-x;
}
struct gra{
int head[N],to[N<<1],nxt[N<<1],tot;
inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}
}A,B;
void tar(int x)
{
int i,j,tp;sz[x]=1;val[x]=-1;
df[x]=low[x]=++dfn;stk[++top]=x;
for(i=A.head[x];i;i=A.nxt[i]){
j=A.to[i];
if(!df[j]){
tar(j);
if(low[j]>=df[x]){
B.lk(x,++num);val[num]=1;//方点权值
for(;;){
tp=stk[top--];sz[num]+=sz[tp];
val[num]++;B.lk(num,tp);
if(tp==j) break;
}
sz[x]+=sz[num];
}else low[x]=min(low[x],low[j]);
}else low[x]=min(low[x],df[j]);
}
}
void dfs(int x)
{
if(x<=n) ans-=(S-1);//圆点自身作为起点
ans+=(ll)(S-sz[x])*sz[x]*val[x];//
for(int j,i=B.head[x];i;i=B.nxt[i]){
j=B.to[i];
ans+=(ll)(S-sz[j])*sz[j]*val[x];//每个子树结点作为起点
dfs(j);
}
}
int main(){
int i,j,x,y;
rd(n);rd(m);num=n;
for(i=1;i<=m;++i){
rd(x);rd(y);
A.lk(x,y);A.lk(y,x);
}
for(i=1;i<=n;++i) if(!df[i]) {tar(i);S=sz[i];dfs(i);}
printf("%lld",ans);
return 0;
}