P4630 [APIO2018] 铁人两项 题解

题目链接: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
}

posted @ 2026-07-14 20:09  MCYYDS  阅读(2)  评论(0)    收藏  举报