P4630 [APIO2018] 铁人两项 题解
主要是讲圆方树的运用(?)
首先把图转成圆方树,然后将圆点的权值设为 -1,方点的权值设为其代表的点双 $ B $ 的大小 $ |B| $。对于一条路径 $ u->v $,可以选的中间点有 $ u->v $ 路径上经过所有方点代表的点双,设有 $ k $ 个,择优 $ k+1 $ 个圆点,方点权值之和为 $ \sum_{i=1}^{k} |B_i| $,减去路径两端的点和中间每两个点双的重叠点有 $ \sum_{i=1}^{k} |B_i| -(k+1) $,恰好等于圆方树上 $ u->v $ 经过的所有点的点权之和。
于是考虑怎么求每个点被经过了多少次,设当前点 $ u $ 所在的圆方树有 $ cnt $ 个点,$ u $ 有 $ j $ 个子树,其父亲(如果没有就是 0)方向包好的点数量为 $ fa_u $,其儿子的第 $ i $ 个子树点数为 $ son_{u,i} $,则有经过他的路径数为 $ cnt(cnt-1) - fa_u(fa_u - 1) - \sum_{i=1}^{j} son_{u,i}(son_{u,i} - 1) $。因为圆方树内点两两形成路径,而子树或父亲方向内部两两形成的路径不经过点 $ u $。
代码:
#include<bits/stdc++.h>
#define time(null) chrono::steady_clock::now().time_since_epoch().count()
#define int long long
#define uint unsigned long long
#define debug() cout<<"come here\n"
#define INF 0x3f3f3f3f3f3f3f3f
#define pii pair<int,int>
#define pb push_back
#define Code return
#define by 0
#define MCYYDS ;
using namespace std;
int qpow(int a,int b,int p=INF){int ret=1;while(b){if(b&1)ret=(ret*a)%p;a=(a*a)%p;b>>=1;}return ret;}
inline int read(){int ret=0,f=1;char ch=getchar();while(ch<'0'||ch>'9')f=(ch=='-'?-1:f),ch=getchar();while(ch>='0'&&ch<='9')ret=(ret<<3)+(ret<<1)+(ch^48),ch=getchar();return ret*f;}
inline void write(int x){if(x<0){putchar('-');write(-x);return ;}if(x>9)write(x/10);putchar((char)(x%10+48));}
inline void writech(int x,char ch){write(x);putchar(ch);}
vector<int> e[100005],tr[200005],ord;
int dfn[100005],low[100005],tot,cnt,w[200005],fa[200005],sz[200005],n,m;
stack<int> stk;
void tarjan(int u,int fa)
{
dfn[u]=low[u]=++tot;
stk.push(u);
for(auto v:e[u])
{
if(!dfn[v])
{
tarjan(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u])
{
int b=++cnt;
tr[u].pb(b);
tr[b].pb(u);
w[b]++;
while(1)
{
int x=stk.top();
tr[x].pb(b);
tr[b].pb(x);
w[b]++;
stk.pop();
if(x==v)break;
}
}
}
else if(v!=fa)low[u]=min(low[u],dfn[v]);
}
}
void dfs(int u,int f)
{
fa[u]=f;
ord.pb(u);
sz[u]=(u<=n);
for(auto v:tr[u])
{
if(v==f)continue;
dfs(v,u);
sz[u]+=sz[v];
}
}
signed main()
{
// ios::sync_with_stdio(0);
// cin.tie(0);
// cout.tie(0);
n=read(),m=read();
for(int i=1;i<=m;i++)
{
int u=read(),v=read();
e[u].pb(v);
e[v].pb(u);
}
cnt=n;
for(int i=1;i<=n;i++)
{
w[i]=-1;
}
for(int i=1;i<=n;i++)
{
if(!dfn[i])tarjan(i,0);
while(stk.size())stk.pop();
}
memset(fa,-1,sizeof(fa));
int ans=0;
for(int i=1;i<=cnt;i++)
{
if(fa[i]!=-1)continue;
ord.clear();
dfs(i,0);
int ctt=0;
for(auto x:ord)
{
if(x<=n)ctt++;
}
for(auto u:ord)
{
int cur=ctt*(ctt-1);
for(auto v:tr[u])
{
int nw=0;
if(fa[v]==u)nw=sz[v];
else nw=ctt-sz[u];
cur-=nw*(nw-1);
}
ans+=cur*w[u];
}
}
writech(ans,'\n');
Code by MCYYDS
}

浙公网安备 33010602011771号