地地铁铁

Description

给定一个无向连通图,边权为 D or d,问有多少对点之间存在同时出现 Dd 的简单路径。

Solution

遇到“简单路径”,考虑点双连通分量缩点 or 圆方树。

\(siz\) 为点双连通分量的大小。

\(u,v\) 在同一个点双连通分量中,且该点双既包含 D 又包含 d

  • 若点双中有且仅有两个点的出边既有 D 也有 d,那么只有这两个点没有同时出现 Dd 的简单路径,贡献为 \(\binom{siz}{2}-1\)

  • 否则,点双中任意两个点都有同时出现 Dd 的简单路径,贡献为 \(\binom{siz}{2}\)

\(u,v\) 不在同一个点双连通分量中,两个点不满足情况当且仅当两点之间点双连通分量的边全为 Dd,用并查集统计只有 Dd 的树上连通块,计算贡献即可。

时间复杂度 \(O(n \log n)\)

点击查看代码
#include<bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define int long long
#define pb push_back
#define rep(i,l,r) for(int i=(l); i<=(r); ++i)
#define drep(i,r,l) for(int i=(r); i>=(l); --i)
using namespace std;
const int N=2e6+5;
char s;
int Sub,n,m,x,y,ans,F[N],msk[N],Fa[N],sz[N];
int dfc,cnt,top,dfn[N],low[N],stk[N];
vector<pair<int,int>>G[N];
unordered_map<int,int>mp[N];
vector<int>g[N];
inline void tarjan(int x) {
	dfn[x]=low[x]=++dfc,stk[++top]=x;
	for(auto [y,c]:G[x]) {
		if(!dfn[y]) {
			tarjan(y),low[x]=min(low[x],low[y]);
			if(dfn[x]<=low[y]) {
				cnt++;
				int z;
				do {
					z=stk[top--],g[z].pb(n+cnt),g[n+cnt].pb(z);
				} while(z!=y);
				g[x].pb(n+cnt),g[n+cnt].pb(x);
			}
		} else low[x]=min(low[x],dfn[y]);
	}
}
inline void dfs(int x,int fa) {
	F[x]=fa;
	for(auto y:g[x]) if(y^fa) dfs(y,x);
}
inline int find(int x) {
	return Fa[x]==x?x:Fa[x]=find(Fa[x]);
}
signed main() {
	FASTIO;
	cin>>Sub>>n>>m;
	rep(i,1,m) cin>>x>>y>>s,G[x].pb({y,s=='d'}),G[y].pb({x,s=='d'});
	tarjan(1),dfs(1,0);
	rep(x,1,n) for(auto [y,c]:G[x])
		if(F[F[x]]==y||F[x]==F[y]) msk[F[x]]|=(1<<c),mp[F[x]][x]|=(1<<c),mp[F[x]][y]|=(1<<c);
	rep(i,n+1,n+cnt) {
		int c=0;
		for(auto [x,v]:mp[i]) if(v==3) c++;
		if(msk[i]==3) ans+=(c==2);
	}
	rep(i,n+1,n+cnt) Fa[i]=i,sz[i]=g[i].size();
	rep(i,1,n) {
		vector<pair<int,int>>v;
		for(auto y:g[i]) if(msk[y]!=3) v.pb({msk[y],y});
		sort(v.begin(),v.end());
		int siz=v.size()-2;
		rep(j,0,siz) {
			int x=find(v[j].second),y=find(v[j+1].second);
			if(msk[v[j].second]==msk[v[j+1].second]&&x!=y) Fa[x]=y,sz[y]+=sz[x]-1;
		}
	}
	rep(i,n+1,n+cnt) if(msk[i]!=3&&find(i)==i) ans+=sz[i]*(sz[i]-1)/2;
	cout<<n*(n-1)/2-ans<<'\n';
}
posted @ 2025-03-07 22:08  _Double10  阅读(53)  评论(1)    收藏  举报