地地铁铁
Description
给定一个无向连通图,边权为 D or d,问有多少对点之间存在同时出现 D 和 d 的简单路径。
Solution
遇到“简单路径”,考虑点双连通分量缩点 or 圆方树。
记 \(siz\) 为点双连通分量的大小。
\(u,v\) 在同一个点双连通分量中,且该点双既包含 D 又包含 d:
-
若点双中有且仅有两个点的出边既有
D也有d,那么只有这两个点没有同时出现D和d的简单路径,贡献为 \(\binom{siz}{2}-1\)。 -
否则,点双中任意两个点都有同时出现
D和d的简单路径,贡献为 \(\binom{siz}{2}\)。
\(u,v\) 不在同一个点双连通分量中,两个点不满足情况当且仅当两点之间点双连通分量的边全为 D 或 d,用并查集统计只有 D 或 d 的树上连通块,计算贡献即可。
时间复杂度 \(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';
}
浙公网安备 33010602011771号