圆方树
圆方树是将图转化成树的一种工具。
在图中找到每一个点双联通分量,用圆点表示原图的点,方点表示每一个点双连通分量,然后将每个圆点和它对应的方点连边,就形成了圆方树。
P4630铁人两项
点双有一个很好的性质:对于一个点双中的两个点,它们的所有简单路径的并集,恰好完全等于这个点双。
因此,两点在原图中的路径集合,就是两点在圆方树中的路径经过的方点的相邻圆点的集合。
因此,当起点和终点固定时,中转点数量等于起点与终点路径并集的点的数量
如何求点的数量?这需要一个圆方树的常用技巧:路径统计时,点赋上合适的权值。
本题中,每个方点的权值为对应点双的大小,而每个圆点权值为
因此,问题转化为统计圆方树上
暴力求是
#include<iostream>
#include<cstdio>
#include<vector>
#define int long long
using namespace std;
const int l=4e5+5;
int n,m,u,v,root,num,tim,tot,cnt,ans,r,siz[l],vis[l],w[l],stk[l],dfn[l],low[l],nxt[l],head[l],ver[l];
bool cut[l];vector <int> p[l];
void add(int x,int y){
nxt[++tot]=head[x];head[x]=tot;ver[tot]=y;
}
void tarjan(int x){
dfn[x]=low[x]=++tim;
++num;
stk[++r]=x;
if(x==root&&head[x]==0){
++cnt;w[cnt]=1;
p[cnt].push_back(x);
p[x].push_back(cnt);
return ;
}
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x]){
++cnt;w[cnt]=0;
int z;
do{
z=stk[r--];
p[cnt].push_back(z);
p[z].push_back(cnt);
++w[cnt];
}while(z!=y);
++w[cnt];
p[cnt].push_back(x);
p[x].push_back(cnt);
}
}
else
low[x]=min(low[x],dfn[y]);
}
}
void dfs(int x,int fa){
vis[x]=1;
siz[x]=(x<=n);
for(int i=0;i<p[x].size();++i){
if(p[x][i]!=fa){
dfs(p[x][i],x);
ans+=2ll*w[x]*siz[x]*siz[p[x][i]];
siz[x]+=siz[p[x][i]];
}
}
ans+=2ll*w[x]*siz[x]*(num-siz[x]);
}
signed main(){
scanf("%lld%lld",&n,&m);
cnt=n;
for(int i=1;i<=n;++i)
w[i]=-1;
for(int i=1;i<=m;++i){
scanf("%lld%lld",&u,&v);
if(u!=v)
add(u,v),add(v,u);
}
for(int i=1;i<=n;++i)
if(!dfn[i])
num=0,root=i,tarjan(i),dfs(i,0);
printf("%lld\n",ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探