题面
题解
比较容易想到建广义圆方树
关键是怎样给点赋权
如果我们枚举了路径的两个端点
那么有多少个中转点是合法的呢?
假设我们枚举到的是两个红色点,那么路径上的合法中转点(蓝色点)就是图中标记的部分
显然,这两点路径上的点双中的每个点都是可以取到的
我们可以把所有方点的权值赋为它所管辖的点双大小
这样,以两个红点为路径端点的合法中转点个数就是其路径的点权和
但是由于路径两端的端点是不能取到的,并且两个方点之间的公共圆点会被重复算
所以还应该将所有圆点的点权赋为-1
还有一个问题,我们不能直接枚举所有的点对来计算贡献
我们先把答案的式子列出来
(P是枚举的路径)
交换求和号
就变成了对经过i点的路径条数的计数问题
简单DP即可
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
inline int gi()
{
char c;int num=0,flg=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
return num*flg;
}
#define N 200005
int n,m;
vector<int> G[N],gid[N];
int fir[N],to[2*N],nxt[2*N],cnt;
void adde(int a,int b)
{
to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;
}
int tot;
int dfn[N],low[N],stk[N],top,pbccnt,dc;
int pbcsiz[N],all;
void dfs(int u,int pp)
{
if(u<=n)all++,pbcsiz[u]=-1;
dfn[u]=low[u]=++dc;
stk[top++]=u;
for(int v,i=0;i<int(G[u].size());i++){
if(gid[u][i]!=pp){
v=G[u][i];
if(!dfn[v]){
dfs(v,gid[u][i]);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]){
adde(u,++tot);pbcsiz[tot]=1;
while(top>0){
adde(tot,stk[--top]);
pbcsiz[tot]++;
if(stk[top]==v)break;
}
}
}
else low[u]=min(low[u],dfn[v]);
}
}
}
#define LL long long
LL f[N],ans;
void DP(int u,int ff)
{
if(u<=n)f[u]=1;
for(int v,p=fir[u];p;p=nxt[p]){
if((v=to[p])!=ff){
DP(v,u);
ans+=1ll*f[u]*f[v]*pbcsiz[u];
f[u]+=f[v];
}
}
ans+=1ll*f[u]*(all-f[u])*pbcsiz[u];
}
int main()
{
int i,u,v;
n=gi();m=gi();
for(i=1;i<=m;i++){
u=gi();v=gi();
G[u].push_back(v);gid[u].push_back(i);
G[v].push_back(u);gid[v].push_back(i);
}
tot=n;
for(i=1;i<=n;i++)
if(!dfn[i]){
all=0;dfs(i,0);
DP(i,0);
}
printf("%lld\n",2ll*ans);
}