首页 > 其他分享 >Codeforces 1799H - Tree Cutting(树形 dp)

Codeforces 1799H - Tree Cutting(树形 dp)

时间:2023-04-29 22:34:09浏览次数:40  
标签:子树 int siz Tree Codeforces Cutting MAXN 操作 dp

思考的时候一直卡在不会在低于 \(O(n)\) 的时间内储存一个连通块的 \(siz\) 有关的信息,看了洛谷题解之后才发现我真是个小丑。

树形 DP。对于一条我们需要操作的边 \((i,fa_i)\),我们将其分为保留子树和删除子树两种类型,对于删除子树,我们在判定其是否合法时候改为判定删除的连通块大小是否为 \(s_{k-1}-s_k\),好处是我们只用关心子树内那一部分的 \(siz\)。

这样考虑 DP,\(dp_{u,S,k}\) 表示考虑 \(u\) 子树,有且仅有 \(S\) 操作集合中的操作对应的 \(i\) 在 \(u\) 子树内,且这些操作中最早的保留子树操作是 \(k\) 的方案数(不存在则 \(k=c+1\))。考虑转移,先考虑怎么合并两个子树 \(u,v\),假设要合并 \(dp_{u,S,k_1}\) 和 \(dp_{v,T,k_2}\),那么可以合并的充要条件是:

  • \(S\cap T=\varnothing\)。
  • \(k_1=c+1\) 或 \(k_2=c+1\)
  • 假设 \(k_1\ne c+1\),那么 \(T\) 中所有元素必须 \(<k_1\)。

然后考虑加入 \((u,fa_u)\) 边的方案数,我们枚举这条边对应的操作 \(i\),那么:

  • 如果这个操作是删除子树,那么要求 \(S\) 中所有元素都 \(<i\),并且当中没有任何一个元素是保留子树的操作。
  • 如果这个操作是保留子树,那么要求其他操作中最早的保留子树的操作的时间 \(>i\)。

最后考虑怎么计算保留/删除的连通块的大小,其实是 trivial 的,因为两种情况都保证了我们在 \(i\) 之前的所有操作都是删除子树的操作,所以就直接拿 \(siz_u\) 减去 \(S\) 中所有 \(<i\) 的元素的 \(s_{j-1}-s_j\) 即可。不需要额外记录 \(O(n)\) 的一维。

const int MAXN=5000;
const int MAXK=6;
const int MAXP=64;
const int MOD=998244353;
int n,k,hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],ec,s[MAXK+3];
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
int dp[MAXN+5][MAXK+3][MAXP+5],siz[MAXN+5],mx[MAXP+5];
void dfs(int x,int f){
	dp[x][k+1][0]=1;siz[x]=1;
	for(int e=hd[x];e;e=nxt[e]){
		int y=to[e];if(y==f)continue;dfs(y,x);siz[x]+=siz[y];
		static int tmp[MAXK+3][MAXP+5];memset(tmp,0,sizeof(tmp));
		for(int i=1;i<=k+1;i++)for(int S=0;S<(1<<k);S++){
			int rst=(1<<k)-1-S;
			for(int T=rst;;--T&=rst){
				if(mx[T]<=i)tmp[i][S|T]=(tmp[i][S|T]+1ll*dp[x][i][S]*dp[y][k+1][T])%MOD;
				if(i!=k+1&&mx[S]<=i)tmp[i][S|T]=(tmp[i][S|T]+1ll*dp[x][k+1][S]*dp[y][i][T])%MOD;
				if(!T)break;
			}
		}
		for(int i=1;i<=k+1;i++)for(int S=0;S<(1<<k);S++)dp[x][i][S]=tmp[i][S];
	}
	if(x!=1){
		static int tmp[MAXK+3][MAXP+5];
		for(int i=1;i<=k+1;i++)for(int S=0;S<(1<<k);S++)tmp[i][S]=dp[x][i][S];
		for(int i=1;i<=k+1;i++)for(int S=0;S<(1<<k);S++)if(dp[x][i][S]){
			for(int j=1;j<i;j++)if(~S>>(j-1)&1){
				int sumsiz=siz[x];bool flg=1;
				for(int l=1;l<j;l++)if(S>>(l-1)&1)sumsiz-=(s[l-1]-s[l]);
				if(sumsiz==s[j])tmp[j][S|(1<<j-1)]=(tmp[j][S|(1<<j-1)]+dp[x][i][S])%MOD;
				if(sumsiz==s[j-1]-s[j]&&mx[S]<j)tmp[i][S|(1<<j-1)]=(tmp[i][S|(1<<j-1)]+dp[x][i][S])%MOD;
			}
		}
		for(int i=1;i<=k+1;i++)for(int S=0;S<(1<<k);S++)dp[x][i][S]=tmp[i][S];
	}
//	for(int i=1;i<=k+1;i++)for(int S=0;S<(1<<k);S++)if(dp[x][i][S])
//		printf("dp[%d][%d][%d]=%d\n",x,i,S,dp[x][i][S]);
}
int main(){
	scanf("%d",&n);for(int i=1,u,v;i<n;i++)scanf("%d%d",&u,&v),adde(u,v),adde(v,u);
	scanf("%d",&k);for(int i=1;i<=k;i++)scanf("%d",&s[i]);s[0]=n;
	for(int i=1;i<(1<<k);i++)for(int j=1;j<=k;j++)if(i>>(j-1)&1)chkmax(mx[i],j);
	dfs(1,0);int res=0;
	for(int i=1;i<=k+1;i++)res=(res+dp[1][i][(1<<k)-1])%MOD;
	printf("%d\n",res);
	return 0;
}

标签:子树,int,siz,Tree,Codeforces,Cutting,MAXN,操作,dp
From: https://www.cnblogs.com/tzcwk/p/Codeforces-1799H.html

相关文章

  • Codeforces 1815E - Bosco and Particle
    首先,对于每个\(s_i\),我们只用保留其最小周期,证明显然。同时以多个光电门为研究对象显然状态数过多,不方便统计。考虑一下连接不同光电门的纽带是什么:显然是相邻光电门之间的空隙。对于每个光电门\(i\),如果我们只保留\(i\)作为唯一的光电门,那么显然有\(0\to1\)和\(1\to2\)......
  • Educational Codeforces Round 1
    A.TrickySum公式求出1到n的和,然后枚举二等整次幂。#include<bits/stdc++.h>usingnamespacestd;#defineintlonglongvoidsolve(){intn;cin>>n;intsum=(1+n)*n/2;for(inti=1;i<=n;i<<=1)sum-=i......
  • Educational Codeforces Round 145 (Rated for Div. 2)
    Preface补题A~D都能秒出,E没看出性质被薄纱了,F感觉是个丁真题随便讨论下就过了后面看了下F的标算是个倍增,感觉Tutorial对性质挖掘的还不够的说A.GarlandSB题,设出现次数最多的颜色个数为\(cm\),若\(cm\le2\)则答案为\(4\);若\(cm=3\)则答案为\(6\),若\(cm=4\)则无解importjav......
  • CodeForces-858#C 题解
    正文♦最坏时间复杂度:\(\mathcal{O}(\lvertS\rvert)\)本题十分简单,但请注意两个条件要同时满足。因为要求分割的次数越少越好,所以只要连续的辅音字母长度不大于2就不需要分割。由于辅音字母太多,只需要标记元音字母即可。#include<iostream>#include<string>#include<cst......
  • Codeforces Round 854 补题总结
    CodeforcesRound854补题总结前言昨天做这套题很不顺,今天补完题大概总结一下。总结RecentActions按题意模拟即可,考虑到前\(n\)个数一定始终在后\(m\)个数的前面,所以说当当前队列中如果没有出现\(x\)而在第\(i\)轮放进了\(x\),那么当前在队首的编号小于\(n\)的数......
  • Codeforces Round 868 Div 2
    A.A-characteristic(CF1823A)题目大意要求构造一个仅包含\(1\)和\(-1\)的长度为\(n\)的数组\(a\),使得存在\(k\)个下标对\((i,j),i<j\)满足\(a_i\timesa_j=1\)。解题思路当有\(x\)个\(1\),\(y\)个\(-1\)时,其满足条件的下标对数量为\(\frac{x(x-1)}{2}......
  • Codeforces Round 868 (Div. 2)
    Preface恭迎废物hl666终于回到了他忠诚的蓝名之位本来这场25min切完前三题而且没挂的时候感觉状态不错的,然后D被自己的一个假做法坑了1h最后ztc想出了大概的构造方法后已经来不及写了,一些细节没考虑好直接爆炸本来当时就有弃D开E的想法的,但可以E的题意在公告出来之前就已经读......
  • 二叉树Binary Tree
    二叉树BinaryTree1.树的一些常用术语2.二叉树的概念树有很多种,每个节点最多只能有两个子节点的一种形式称为二叉树;二叉树的子节点分为左子节点和右子节点;以下三种均为二叉树:若该二叉树的所有叶子节点都在最后一层,且节点总数n==\(2^k\)-1,k为层数,则称为满二叉树......
  • Codeforces Round 868 (Div. 2) A-E题解
    比赛地址这把真不在状态,麻了,看来还得再练A.A-characteristic题意:给出n和k,要求构造只含1和-1数组a,存在k对(i,j)(i≠j),有a[i]*a[j]=1Solution令构造的数组有x个1和y个-1,那么其对于答案的贡献有\[x*(x-1)/2+y*(y-1)/2\]又因为有x+y=n,所以可以转化成关于x的一元二次方程化简后......
  • 现在告诉你MySQL为什么选择B+Tree呢?
    大家都知道MySQL数据库选择的是B+Tree作为索引的数据结构,那为什么会选择B+Tree呢?本文分四种数据结构来分析:二叉查找树平衡二叉树多路平衡查找树加强版多路平衡查找树(B+Tree)二叉查找树二叉搜索树的特点:左子树的键值小于根的键值,右子树的键值大于根的键值。   从上面的2个图来看......