Description
给出一张n个点,m条边的简单无向图,求有多个三元组(s,f,t),满足两两互不相等且存在至少一条从s出发经过f到t的不经过重复点的路径。
n<=1e5,m<=2e5
Solution
考场都去做A了没仔细想C,以为很麻烦
其实把圆方树弄出来就变得很简单了:考虑枚举中间点f,如果f是圆点就和树一样的做法,如果f是方点表示中间点可以是这个点双中除去起点和终点的所有点,因为中间点是这两个点中的一个的情况会在枚举到这两个点的时候计算到。
然后就随便搞搞就好了,注意圆方树建树的时候需要把点双的根单独加进来。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll;
int read() {
char ch;
for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
int x=ch-'0';
for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x;
}
const int N=4e5+5;
int n,m,dfn[N],low[N],stack[N],cnt[N],siz[N],sz,tmp,tot,top;
bool vis[N],used[N];
ll ans;
struct Graph{
int to[N<<1],nxt[N<<1],lst[N],l;
void add(int x,int y) {to[++l]=y;nxt[l]=lst[x];lst[x]=l;}
}G,T;
void tarjan(int x) {
dfn[x]=low[x]=++tot;stack[++top]=x;
for(int i=G.lst[x];i;i=G.nxt[i])
if (!vis[i]) {
int y=G.to[i];
vis[i]=vis[i^1]=1;
if (!dfn[y]) {
tarjan(y);
low[x]=min(low[x],low[y]);
if (dfn[x]<=low[y]) {
++tmp;int z;
do {
z=stack[top--];cnt[tmp]++;
T.add(z,tmp);T.add(tmp,z);
} while (z!=y);
cnt[tmp]++;T.add(tmp,x);T.add(x,tmp);
}
} else low[x]=min(low[x],dfn[y]);
}
}
void dfs(int x,int y) {
siz[x]=x<=n;
for(int i=T.lst[x];i;i=T.nxt[i]) {
int z=T.to[i];
if (z!=y) {
dfs(z,x);
siz[x]+=siz[z];
}
}
}
void calc(int x,int y) {
int now=0;used[x]=1;
for(int i=T.lst[x];i;i=T.nxt[i]) {
int z=T.to[i];
if (z!=y) {
calc(z,x);
ans+=(ll)2*siz[z]*now*((x<=n)?1:(cnt[x]-2));
now+=siz[z];
}
}
if (y) ans+=(ll)2*now*(sz-siz[x])*((x<=n)?1:(cnt[x]-2));
}
int main() {
n=read();m=read();G.l=1;
fo(i,1,m) {
int x=read(),y=read();
G.add(x,y);G.add(y,x);
}
tmp=n;fo(i,1,n) if (!dfn[i]) tarjan(i);
fo(i,1,tmp) if (!used[i]) dfs(i,0),sz=siz[i],calc(i,0);
printf("%lld\n",ans);
return 0;
}