树和森林

树和森林

题解

很好骗分的一道题,有人就在场上骗到了90pts,例子就是我

我们可以先来看一看部分分。

14pts:我们直接枚举两个连通块上相连的点,O\left(n^2 \right )的算法。

20pts:很容易发现,在两个联通块上互相相连的点是谁并不重要,记一个点到块上每个点的路径长度总和为f_{u},记一个联通块中最大的f值为mxf_{i},答案就是sz_{1}sz_{2}+sz_{1}mxf_{2}+sz_{2}mxf_{1}+sumf_{1}+sumf_{2},其中sumf表示这个连通块自身的贡献,f值可以用一个换根dp来求出,时间复杂度O\left(n \right )

30pts:再将三个联通块中的中间的连通块与其它连通块相连接的点枚举,答案为\tiny (mxf_{a}+sz_{a})(n-sz_{a})+(mxf_{c}+sz_{c})(n-sz_{c})+d_{b1,b2}sz_{a}sz_{c}+f_{b1}sz_{a}+f_{b2}sz_{c}+sumf_{a}+sumf_{b}+sumf_{c}

其中点b为中间块。由于d_{b1,b2}可以通过倍增求出,时间复杂度O\left(n^2log_{n})

70pts:对于子任务2,我们可以通过树形dp快速的求出,只用判断该节点与其子节点是否相连即可,分别存储该节点与其儿子连边为奇数或是偶数,跑一遍dfs。时间复杂度O\left(n \right )

90pts:容易证明,对于一棵树,要么其无法满足拆分条件,要么有唯一解。因为叶节点的奇偶性是固定的,故其父亲节点的来连边状况也是固定的,若根节点符合条件则有唯一解,否则无解,用70pts的做法可以求出。于是笔者就得到了90pts。

100pts:我们发现,在30pts的做法中,只有d_{b1,b2}sz_{a}sz_{c}+f_{b1}sz_{a}+f_{b2}sz_{c}一项不是固定的,其它都可以预处理出来,我们只用考虑如何求出最大的d_{b1,b2}sz_{a}sz_{b}+f_{b1}sz_{a}+f_{b1}sz_{c}。我们可以将d_{b1,b2}拆成dep_{b1}+dep_{b2}-2dep_{lca(b1,b2)}。于是,原式就成了sz_{a}(dep_{b1}sz_{c}+f_{b1})+sz_{c}(dep_{b2}sz_{a}+f_{b2})-2dep_{lca(b1,b2)}sz_{a}sz_{c}。我们可以在dfs的过程中,对于每个节点求出它作为lca时上式的最大值,可以通过树形dp来维护。

源码

笔者代码有些冗长。

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define MAXN 100005
typedef long long LL;
typedef unsigned long long uLL;
const int INF=0x7f7f7f7f;
typedef pair<int,int> pii;
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;
}
int n,m,rt[5],sz[5],dep[MAXN];
LL mf[MAXN],sumf[MAXN],f[MAXN],g[MAXN],siz[MAXN];
LL mx1[MAXN],mx2[MAXN],mx;
int dp[MAXN][2],fi[MAXN],sta[MAXN],stak,ff[MAXN][20];
bool col[MAXN],dw[MAXN][2];char maze[MAXN];
int belong[MAXN],cnt,head[MAXN],tot;
struct edge{int to,nxt,id;}e[MAXN<<1];
void addEdge(int u,int v,int i){e[++tot]=(edge){v,head[u],i};head[u]=tot;}
void dfs1(int u,int fa){
	g[u]=0;sz[belong[u]]++;siz[u]=1;dep[u]=dep[fa]+1;
	ff[u][0]=fa;for(int i=1;i<19;i++)ff[u][i]=ff[ff[u][i-1]][i-1];
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;if(v==fa)continue;
		belong[v]=cnt;dfs1(v,u);
		siz[u]+=siz[v];g[u]+=g[v]+siz[v];
	}
}
void dfs2(int u,int fa){
	f[u]=g[u];
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;if(v==fa)continue;
		g[u]-=g[v]+siz[v];siz[u]-=siz[v];
		g[v]+=g[u]+siz[u];siz[v]+=siz[u];
		dfs2(v,u);
		siz[v]-=siz[u];g[v]-=g[u]+siz[u];
		siz[u]+=siz[v];g[u]+=g[v]+siz[v];
	}
}
void dfs3(int u,int fa){
	int res=0,md=INF,sum=0,mdd=-1;
	if(!head[u]){dp[u][0]=0;dp[u][1]=-INF;return ;}
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;if(v==fa)continue;
		fi[v]=e[i].id;dfs3(v,u);
		if(col[v]==1){
			res+=max(dp[v][1],dp[v][0]+1);
			if(dp[v][0]+1>=dp[v][1])sum++;
			if(dp[v][1]>=0&&dp[v][0]>=0){
				if(Fabs(dp[v][1]-dp[v][0]-1)<md);
					md=Fabs(dp[v][1]-dp[v][0]-1),mdd=v;
			}
		}
		else{
			res+=max(dp[v][0],dp[v][1]+1);
			if(dp[v][1]+1>=dp[v][0])sum++;
			if(dp[v][0]>=0&&dp[v][1]>=0){
				if(Fabs(dp[v][1]-dp[v][0]-1)<md)
					md=Fabs(dp[v][0]-dp[v][1]-1),mdd=v;
			}
		}
	}
	if(sum&1)dp[u][1]=res,dp[u][0]=(md<=res?res-md:-INF);
	else dp[u][0]=res,dp[u][1]=(md<=res?res-md:-INF);
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;if(v==fa)continue;
		if(col[v]==1){
			if(dp[v][0]+1>=dp[v][1]){
				dw[v][sum&1]=1;
				if(mdd!=v)dw[v][(sum&1)^1]=1;
			}
			else if(mdd==v)dw[v][(sum&1)^1]=1;
		}
		else{
			if(dp[v][1]+1>=dp[v][0]){
				dw[v][sum&1]=1;
				if(mdd!=v)dw[v][(sum&1)^1]=1;
			}
			else if(mdd==v)dw[v][(sum&1)^1]=1;
		}
	}
}
int lca(int a,int b){
	if(dep[a]>dep[b])swap(a,b);
	for(int i=18;i>=0;i--)if(dep[ff[b][i]]>=dep[a])b=ff[b][i];
	if(a==b)return a;
	for(int i=18;i>=0;i--)if(ff[a][i]!=ff[b][i])a=ff[a][i],b=ff[b][i];
	return ff[a][0];
}
void dfs4(int u,int fa,int s){
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;if(v==fa)continue;
		if(dw[v][s])sta[++stak]=fi[v],dfs4(v,u,col[v]^1);
		else dfs4(v,u,col[v]);
	}
}
void dfs5(int u,int fa,int a,int b){
	mx1[u]=f[u]*a;mx2[u]=f[u]*b;mx=max(mx,mx1[u]+mx2[u]);
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;if(v==fa)continue;
		dfs5(v,u,a,b);LL c=1ll*a*b;
		mx=max(mx,mx1[u]+mx2[v]+c);
		mx=max(mx,mx2[u]+mx1[v]+c);
		mx1[u]=max(mx1[u],mx1[v]+c);
		mx2[u]=max(mx2[u],mx2[v]+c);
	}
}
LL getmx(int rt,int a,int b){mx=0;dfs5(rt,0,a,b);return mx;}
signed main(){
	//freopen("lct.in","r",stdin);
	//freopen("lct.out","w",stdout);
	read(n);read(m);scanf("\n%s",maze+1);
	for(int i=1;i<=n;i++)col[i]=(maze[i]=='B');
	for(int i=1;i<=m;i++){
		int u,v;read(u);read(v);
		addEdge(u,v,i);addEdge(v,u,i);
	}
	for(int i=1;i<=n;i++)
		if(!belong[i]){
			belong[i]=++cnt,rt[cnt]=i;
			dfs1(i,0);dfs2(i,0);
		}
	for(int i=1;i<=n;i++)mf[belong[i]]=max(mf[belong[i]],f[i]);
	for(int i=1;i<=n;i++)sumf[belong[i]]+=1ll*f[i];
	for(int i=1;i<=cnt;i++)sumf[i]/=2LL;
	if(cnt==2)printf("%lld\n",sumf[1]+sumf[2]+1ll*mf[1]*sz[2]+1ll*mf[2]*sz[1]+1ll*sz[1]*sz[2]);
	else{
		LL res=0;
		res=max(res,(mf[2]+sz[2])*(n-sz[2])+(mf[3]+sz[3])*(n-sz[3])+getmx(rt[1],sz[2],sz[3]));
		res=max(res,(mf[1]+sz[1])*(n-sz[1])+(mf[2]+sz[2])*(n-sz[2])+getmx(rt[3],sz[1],sz[2]));
		res=max(res,(mf[1]+sz[1])*(n-sz[1])+(mf[3]+sz[3])*(n-sz[3])+getmx(rt[2],sz[1],sz[3]));
		printf("%lld\n",res+sumf[1]+sumf[2]+sumf[3]);
	}
	for(int i=1;i<=cnt;i++)dfs3(rt[i],0);int ans=0;
	for(int i=1;i<=cnt;i++){ 
		if(dp[rt[i]][col[rt[i]]]<0)ans=-1;
		if(ans<0)continue; 
		ans+=dp[rt[i]][col[rt[i]]];
	} 
	printf("%d\n",ans);if(ans<0)return 0;
	for(int i=1;i<=cnt;i++)dfs4(rt[i],0,col[rt[i]]);
	sort(sta+1,sta+stak+1);
	for(int i=1;i<=stak;i++)printf("%d ",sta[i]);puts("");
	return 0;
}

谢谢!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值