题解:P8456 「SWTR-8」地地铁铁
更好的阅读体验
圆方树入门题。听讲了四次才会做。
假设 D 是 d 是
先考虑起点和终点在同一个点双的情况。假设当前点双的大小为
那么我们考虑如果点双内部含有两种边,那么是否每个点对都合法呢?好像也不是。如图,这个点双内部
所以经过手玩我们会发现,如果点双内只有两个点同时具有
所以对于一个大小为
接下来考虑起终点不在同一个点双的情况,对于这种情况我们在圆方树上分析。
对于一个方点,如果内部只有
那么如果一条路径上只有白点或者只有黑点,那就已经寄了,因为不管怎么走都走不到其他颜色的边,因此一定是不合法的路径。
很显然一条路径上同时有白点和黑点的话,一定是合法的。
接下来考虑加入灰点怎么办。如果这个灰点内部有超过
那如果有像上图的情况怎么办?假设灰点中,进入点双的点和出点双的点之间有若干条含有两种颜色的路径,但是每条路径只有一种颜色,那么我们可以根据我们方点外我们经过了哪种颜色,在这个灰点中选择另一种颜色,就可以了。
所以,对于起终点在不同点双的情况,答案就是路径上只有白点或只有黑点的路径数量。
所以我们就对白点和黑点分别求一个极大的连通块,假设某个连通块含有的圆点数量为
再说一下怎么求一条边属于哪个点双。如果直接枚举出边会被菊花卡成
我们在圆方树上随便取一个根,求出每个点的父亲
对于原图一条边
所以如果
最后拿总方案数
#include<bits/stdc++.h>
#define endl '\n'
#define N 1000006
using namespace std;
int _,n,m,tp,dfs_clock,dn;
int dfn[N],tf[N],low[N],stk[N],col[N],fa[N],sz[N],cnt[N][2],c[N],vis[N];
long long ans;
vector<int> tG[N],bcc[N]; vector<pair<int,int> > G[N];
inline void add(int u,int v) {tG[u].push_back(v),tG[v].push_back(u);}
inline long long binom2(int x) {return 1ll*x*(x-1)/2;}
inline int find(int k) {return fa[k]==k?k:fa[k]=find(fa[k]);}
inline void merge(int u,int v) {u=find(u),v=find(v); if(u^v)fa[v]=u,sz[u]+=sz[v];}
inline int get(int u,int v)
{
if(tf[u]==tf[v])return tf[u];
else if(tf[tf[u]]==v)return tf[u];
else if(tf[tf[v]]==u)return tf[v];
assert(0);
}
void tarjan(int u)
{
dfn[u]=low[u]=++dfs_clock,stk[++tp]=u;
for(auto vv:G[u])if(!dfn[vv.first])
{
int v=vv.first;
tarjan(v),low[u]=min(low[u],low[v]);
if(low[v]==dfn[u])
{
dn++;
for(int x=-1;x!=v;tp--)
x=stk[tp],add(x,dn),bcc[dn].push_back(x);
add(u,dn),bcc[dn].push_back(u);
}
} else low[u]=min(low[u],dfn[vv.first]);
}
void dfs(int u) {for(int v:tG[u])if(v!=tf[u])tf[v]=u,dfs(v);}
void solve(int o)
{
for(int i=1;i<=dn;i++)fa[i]=i,sz[i]=0;
for(int i=1;i<=n;i++)sz[i]=1;
for(int i=n+1;i<=dn;i++)if(col[i]==o)
for(int j:tG[i])merge(i,j);
for(int i=1;i<=dn;i++)if(find(i)==i)ans-=binom2(sz[i]);
}
main()
{
scanf("%d%d%d",&_,&n,&m),dn=n;
for(int i=1,u,v;i<=m;i++)
{
char ch[3]; scanf("%d%d%s",&u,&v,ch+1);
G[u].push_back({v,ch[1]=='d'});
G[v].push_back({u,ch[1]=='d'});
}
ans=binom2(n),tarjan(1),dfs(1);
for(int u=1;u<=n;u++)
for(auto v:G[u])cnt[get(u,v.first)][v.second]++;
for(int i=n+1;i<=dn;i++)
if(!cnt[i][1])col[i]=0;
else if(!cnt[i][0])col[i]=1; else col[i]=2;
memset(cnt,0,sizeof(cnt));
for(int u=1,b;u<=n;u++)
{
for(auto v:G[u])cnt[get(u,v.first)][v.second]++;
for(auto v:G[u])
if(!vis[b=get(u,v.first)]&&cnt[b][0]&&cnt[b][1])c[b]++,vis[b]=1;
for(auto v:G[u])
{
vis[b=get(u,v.first)]=0;
for(int i=0;i<2;i++)cnt[b][i]=0;
}
}
for(int i=n+1;i<=dn;i++)if(c[i]==2)ans--;
solve(0),solve(1);
printf("%lld\n",ans);
return 0;
}