首页 > 其他分享 >[SHOI2017] 摧毁“树状图”

[SHOI2017] 摧毁“树状图”

时间:2024-12-21 09:54:06浏览次数:7  
标签:cnt fmx 树状 int max 摧毁 SHOI2017 mx dp

首先只要得到 \(x=0\) 时的答案,就可以 \(AC\) 本题。这是很重要的。

考虑由于不能有重复经过的边,所以两路径交点数量 \(\le 1\)。

容易想到设 \(dp_u\) 表示以 \(u\) 为端点的链中的贡献最大值。考虑换根 \(dp\),所以先设它只表示它子树内的部分。

当交点数量 \(=1\) 时,显然可以理解为从一个节点出发的 \(0-4\) 条链,要这些链产生的贡献最大,所以要维护每个点的前四大链,这里的大指 \(dp\) 值大。

当交点数量 \(=0\) 时,可以看作是子树内有一条链,子树外有一条链的情况。

那么首先必须要维护前四大链 \(mx_{x,0/1/2/3}\)。

其次由于删掉一个点会产生儿子 \(+1\) 那么多个联通块,所以还要维护儿子数量 \(cnt_i\)。

考虑到子树内有一条链也得维护,所以得维护 \(f_i\) 表示过子树根的路径最大贡献,\(g_i\) 表示不一定过子树根的路径最大贡献。

由于 \(g_i\) 不好维护,所以再设 \(d_i\) 表示我们认为删掉 \(i\) 后 \(i\) 外还有联通块时,路径的最大贡献。那么就有:

\[\begin{cases} dp_x=cnt_i+\max(0,mx_{x,0}-1)\\ f_x=\max(dp_x,mx_{x,0}+mx_{x,1}+cnt_x-2)\\ g_x=\max(\max\limits_{y\in sn_x}d_y,f_x)\\ d_x=\max(\max\limits_{y\in sn_x}d_y,f_x+1) \end{cases} \]

\(\max\limits_{y\in sn_x}d_y\) 可以通过预处理得到。换根时式子相同,所有定义中“子树内”全部改为“以该点为根的树”即可。

时间复杂度 \(O(tn)\)。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,ans,mx[N][5],fmx[N][3];
int t,id,f[N],dp[N],g[N],d[N];
vector<int>ve[N];
void add(int x,int v){
	mx[x][4]=v;
	for(int i=3;~i;i--)
		if(mx[x][i+1]>mx[x][i])
			swap(mx[x][i+1],mx[x][i]);
}void adf(int x,int v){
	fmx[x][2]=v;
	for(int i=1;~i;i--)
		if(fmx[x][i+1]>fmx[x][i])
			swap(fmx[x][i+1],fmx[x][i]);
}void dfs1(int x,int fa){
	int cnt=ve[x].size()-1;
	for(auto y:ve[x]) if(y!=fa)
		dfs1(y,x),add(x,dp[y]),adf(x,d[y]);
	dp[x]=cnt+max(1,mx[x][0])-1;
	f[x]=max(dp[x],mx[x][0]+mx[x][1]+cnt-2);
	g[x]=max(fmx[x][0],f[x]);
	d[x]=max(fmx[x][0],f[x]+1);
}void dfs2(int x,int fa){
	int sz=ve[x].size(),cnt=sz-1;
	for(int i=0;i<5;i++)
		ans=max(ans,sz),sz+=mx[x][i]-1;
	for(auto y:ve[x]){
		if(y==fa) continue;
		int mo=mx[x][0]+mx[x][1];
		int mz=mx[x][0],mt=fmx[x][0];
		if(dp[y]==mx[x][0])
			mo+=mx[x][2]-mz,mz=mx[x][1];
		if(dp[y]==mx[x][1]) mo=mz+mx[x][2];
		if(fmx[x][0]==d[y]) mt=fmx[x][1];
		dp[x]=cnt+max(1,mz)-1;
		f[x]=max(dp[x],mo+cnt-2),add(y,dp[x]);
		g[x]=max(mt,f[x]),d[x]=max(mt,f[x]+1);
		adf(y,d[x]),ans=max(ans,g[x]+f[y]),dfs2(y,x);
	}
}void solve(){
	for(int i=1;i<=n;i++){
		ve[i].clear(),g[i]=d[i]=f[i]=dp[i]=0;
		mx[i][0]=mx[i][1]=mx[i][2]=mx[i][3]=0;
		fmx[i][0]=fmx[i][1]=0;
	}cin>>n;int tmp;
	for(int i=0;i<id*2;i++) cin>>tmp;
	for(int i=1,x,y;i<n;i++)
		cin>>x>>y,ve[x].push_back(y),ve[y].push_back(x);
	ans=0,dfs1(1,0),dfs2(1,0),cout<<ans<<"\n";
}int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>t>>id;
	while(t--) solve();
	return 0;
} 

标签:cnt,fmx,树状,int,max,摧毁,SHOI2017,mx,dp
From: https://www.cnblogs.com/chang-an-22-lyh/p/18620413/shoi2017-cui_hui_shu_zhuang_tu-tj

相关文章

  • 树状数组学习笔记
    位运算是补码进行运算的因此可以解释负数进行位运算时的奇妙现象补码:正数的补码就是其本身负数的补码是在其原码的基础上,符号位不变,其余各位取反,最后+1.(即在反码的基础上+1)E:原码:10000001;补码:01111111.lowbit:lowbit这个函数的功能就是求某一个数的二进制表示......
  • 树状数组详解
    概述树状数组(BinaryIndexedTree,简称BIT),是一种数据结构,用于处理区间查询和更新问题。它是一种可以高效地在对数级别时间复杂度内进行单点更新和区间查询的数据结构。树状数组通常用于解决以下两类问题:区间和查询:给定一个序列,查询序列中任意区间的和。区间更新:给定一个序......
  • 二维树状数组小记
    做LG1527,发现自己竟然还不会二维树状数组,赶紧恶补一下。下面的内容是随手写的,原理不是很难单点改,区间查voidupdate(intx,inty,intv){ for(inti=x;i<=n;i+=lowbit(i)){ for(intj=y;j<=m;j+=lowbit(j)) c[i][j]+=v; }}intgetsum(intx,inty,intv=0){ for(int......
  • 【寻迹#7】树状数组
    树状数组一、简介树状数组是一种支持单点修改和区间查询的,代码量小的数据结构。普通树状数组维护的信息及运算要满足结合律且可差分,如加法(和)、乘法(积)、异或等。事实上,树状数组能解决的问题是线段树能解决的问题的子集:树状数组能做的,线段树一定能做;线段树能做的,树状数组......
  • 一个简单的整数问题(树状数组区间修改,单点查询)
    给定长度为 NN 的数列 AA,然后输入 MM 行操作指令。第一类指令形如 Clrd,表示把数列中第 l∼rl∼r 个数都加 dd。第二类指令形如 Qx,表示询问数列中第 xx 个数的值。对于每个询问,输出一个整数表示答案。输入格式第一行包含两个整数 NN 和 MM。第二行包含......
  • 从 动态前缀和 到 树状数组
    一.引言——动态前缀和前缀和求解我会在最后给出,想看的直接翻到最后,这里默认大家都知道前缀和怎么求解。有这么一个场景,我们需要不断修改数组中的元素,并且问我们数组内某个区间的和。如果使用最原始的前缀和来求解,每次我们都要重新求一遍sum[],更新时间复杂度是O(n)的,查询是O(1)的......
  • 树状数组进阶
    树状数组是众多数据结构中常数较小,简单好写的,很多ds题都应该优先考虑树状数组。接下来介绍树状数组的几种常见套路。单点加,区间求和区间加,单点查询区间加,区间求和二维树状数组,包括上面\(3\)个操作单点修改,求全局\(k\)小值求前驱,后继,排名等平衡树操作......
  • 树状数组
    树状数组作用:动态地维护前缀和查询Timecomplexity修改一个数:$$o(lgn)$$查询一段区间和:$$o(lgn)$$实现过程1lowbit返回一个数的二进制下末尾第一个1和后面的0构成的数如11011000100返回100=4intlowbit(intx){ returnx&(-x);}2建立树状数组定义$$t[x]$$保存......
  • 树状数组
    前缀和之树状数组树状数组(FenwickTree)是一种用于高效处理区间查询与修改的重要工具。它可以在(O(logn))的时间复杂度内完成单点更新和前缀区间求和的操作。一、树状数组的基本思想树状数组通过一个辅助数组(c[i])实现,将原数组的信息以一种特殊的方式存储,使得查询和更新都......
  • 二维树状数组
    更新日志思路和一维没有多大区别。插入时,双重循环,分别循环两个维度。查询时同理。细节如果查询区间和用二维前缀和的方法即可。模板structfenwick{lldat[N][N];intlowbit(intx){returnx&-x;}voidadd(intx,inty,llv){for(inti=x;i<=n;......