[CF980F]Cactus to Tree

Cactus to Tree

题解

换根dp板子

首先看到这种求出每个节点到叶节点上的最大距离值最小,很容易想到换根。
我们考虑对于一个节点,他什么时候在图上可以使得叶节点与它的距离最小。
很明显对于任意节点,它到其他节点的最短路是不冲突的,我们在这个图上建出它的 b f s bfs bfs树时一定是最小的,相当于我们要在这个图上再到这个点到其他所有点最短路的最大值。
由于这图保证每个点属于且仅属于一个环,所以它大概是长这样的:
在这里插入图片描述
我们可以先以某个点为根,求出每个点到它环外的子树 (其实就是再往下的部分) 上的点的距离最大值。
再对原树进行缩点,将一个环缩成一个点,进行换根。

当换根到某个点时,我们可以将它所属环上的所有点的答案一并求出。
对于这环上的每个点,先暴力枚举其儿子,求出其到它的环外子树中叶节点的距离最大值,再考虑环上点的贡献。
g i g_{i} gi表示点 i i i的环外子树中叶节点的距离最大值,很明显有,
a n s i = max ⁡ j ∈ V ( g v + d i s t i , j ) ans_{i}=\max_{j\in V}\left(g_{v}+dist_{i,j}\right) ansi=jVmax(gv+disti,j)
由于 d i s t dist dist在环上分为两段递增,往左往右都是递增,直到环的另一侧与其相对应的点为止。
所以我们可以考虑用单调队列维护环上的点的答案转移。

从一个环换到另一个环时,我们只需要将两环之间桥上在原环上的对应点求出最大值与次大值,看应该去哪一个即可。
注意不能光求子树上的距离,还要注意环上的距离,通过之前求的 a n s ans ans也就可以得知环上的最大值了。

时间复杂度 O ( n + m ) O\left(n+m\right) O(n+m)
不过笔者求环上点时是跟据dfs序排序得到的,所以还要加上一个 l o g log log
虽然题目并没有给 m m m的范围,但由于一个点只属于一个环可知 m ⩽ 3 2 n m\leqslant \frac{3}{2}n m23n,也就是 n 2 \frac{n}{2} 2n个二元环构成链是取最大值。

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 500005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long LL;
typedef unsigned long long uLL;
const int INF=0x3f3f3f3f;
const int mo=998244353;
const int inv2=499122177;
const int jzm=2333;
const int orG=3,invG=332748118;
const int lim=300000;
const double Pi=acos(-1.0);
typedef pair<int,int> pii;
const double PI=acos(-1.0);
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int add(int x,int y){return x+y<mo?x+y:x+y-mo;}
int qkpow(int a,int s){int t=1;while(s){if(s&1)t=1ll*a*t%mo;a=1ll*a*a%mo;s>>=1;}return t;}
int n,m,head[MAXN],tot,dp[MAXN],g[MAXN],dfn[MAXN],low[MAXN],belong[MAXN];
int sta[MAXN],stak,idx,cnt,q[2][MAXN<<1],Head[2],Tail[2],ans[MAXN];
int dis[MAXN],ord[MAXN],tid,mxd[MAXN];
bool vis[MAXN];
vector<int>cir[MAXN]; 
queue<int>Q;
struct edge{int to,nxt;}e[MAXN*3];
void addEdge(int u,int v){e[++tot]=(edge){v,head[u]};head[u]=tot;}
void bfs(int s){
	while(!Q.empty())Q.pop();Q.push(s);dis[s]=1;vis[s]=1;
	while(!Q.empty()){
		int t=Q.front();Q.pop();ord[++tid]=t;
		for(int i=head[t];i;i=e[i].nxt){
			int v=e[i].to;if(vis[v])continue;
			Q.push(v);vis[v]=1;dis[v]=dis[t]+1;
		}
	}
	for(int i=tid;i>0;i--){
		int u=ord[i];mxd[u]=dis[u];
		for(int j=head[u];j;j=e[j].nxt)
			mxd[u]=max(mxd[e[j].to],mxd[u]);
		dp[u]=mxd[u]-dis[u]+1;
	}
}
void tarjan(int u,int fa){
	dfn[u]=low[u]=++idx;sta[++stak]=u;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(!dfn[v])tarjan(v,u),low[u]=min(low[u],low[v]);
		else if(v!=fa)low[u]=min(low[u],dfn[v]);
	}
	if(low[u]==dfn[u]){++cnt;int v;do{v=sta[stak--];belong[v]=cnt;}while(v!=u);}
} 
void dosaka(int u,int fa){
	int id=belong[u],siz=cir[id].size();Head[0]=Tail[0]=Head[1]=Tail[1]=1;
	for(int i=0;i<siz;i++){
		g[i]=1;
		for(int j=head[cir[id][i]];j;j=e[j].nxt)
			if(belong[e[j].to]!=id)g[i]=max(dp[e[j].to]+1,g[i]);
	}
	for(int i=1;i+i<=siz;i++){
		while(Head[0]<Tail[0]&&g[i]+i>=g[q[0][Tail[0]-1]]+q[0][Tail[0]-1])Tail[0]--;
		q[0][Tail[0]++]=i;
	}
	for(int i=(siz+1)/2;i<siz;i++){
		while(Head[1]<Tail[1]&&g[i]-i>=g[q[1][Tail[1]-1]]-q[1][Tail[1]-1])Tail[1]--;
		q[1][Tail[1]++]=i;
	}
	if(Head[0]<Tail[0])ans[u]=max(ans[u],g[q[0][Head[0]]]+q[0][Head[0]]);
	if(Head[1]<Tail[1])ans[u]=max(ans[u],g[q[1][Head[1]]]+siz-q[1][Head[1]]);
	for(int i=1;i<siz;i++){
		int stp=cir[id][i],las=i-1,lst=(i+(siz+1)/2-1+siz)%siz;
		while(Head[1]<Tail[1]&&q[1][Head[1]]==lst)Head[1]++;
		while(Head[0]<Tail[0]&&q[0][Head[0]]==i)Head[0]++;
		while(Head[1]<Tail[1]&&g[q[1][Tail[1]-1]]+(i+siz-q[1][Tail[1]-1])%siz<=g[las]+(i+siz-las)%siz)Tail[1]--;q[1][Tail[1]++]=las;
		while(Head[0]<Tail[0]&&g[q[0][Tail[0]-1]]+(q[0][Tail[0]-1]+siz-i)%siz<=g[lst]+(lst+siz-i)%siz)Tail[0]--;q[0][Tail[0]++]=lst;
		if(Head[0]<Tail[0])ans[stp]=max(ans[stp],g[q[0][Head[0]]]+(q[0][Head[0]]-i+siz)%siz);
		if(Head[1]<Tail[1])ans[stp]=max(ans[stp],g[q[1][Head[1]]]+(i+siz-q[1][Head[1]])%siz);	
	}
	for(int i=0;i<siz;i++){
		u=cir[id][i];int f1=1,f2=1;
		for(int j=head[u];j;j=e[j].nxt){
			int v=e[j].to;if(belong[v]==id)continue;
			if(dp[v]+1>f1)f2=f1,f1=dp[v]+1;
			else if(dp[v]+1>f2)f2=dp[v]+1; 
		}
		f1=max(ans[u],f1);f2=max(ans[u],f2);ans[u]=max(f1,ans[u]);
		for(int j=head[u];j;j=e[j].nxt){
			int v=e[j].to;if(belong[v]==id||v==fa)continue;
			int tmpu=dp[u];if(dp[v]+1==f1)dp[u]=f2;else dp[u]=f1;
			dosaka(v,u);dp[u]=tmpu;
		}
	}
}
bool cmp(int x,int y){return dfn[x]<dfn[y];}
signed main(){
	read(n);read(m);
	for(int i=1;i<=m;i++){
		int u,v;read(u);read(v);
		addEdge(u,v);addEdge(v,u);
	}
	bfs(1);for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i,0);
	for(int i=1;i<=n;i++)cir[belong[i]].push_back(i);
	for(int i=1;i<=cnt;i++)sort(cir[i].begin(),cir[i].end(),cmp);
	dosaka(1,0);for(int i=1;i<=n;i++)printf("%d ",ans[i]-1);puts("");
	return 0; 
} 

谢谢!!!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值