pyyzDay8

大炮2

T1 Centroids

先对一个点进行考虑

若一个点不是树的重心

最多有一个子树>=n/2

所以要在这个子树内删一个<=n/2的siz最大的子树

dp转移即可

问题是现在不定根

如何换根

另记一个子树外<=n/2的siz最大的子树

进行换根

T2 Road Improvement

换根DP时记录0的个数

或者维护前缀和后缀

T3 Adam and Tree

考虑dp记录答案

每次询问时暴力向上修改链的答案

但这是O(N^2)的

考虑优化

若跳到某个点,值没有更新,则就结束更新

这样就是对的

为什么?

考虑树剖,fi的和<=nlogn

我们的答案一定<=树剖的答案

即时间复杂度一定<=O(nlogn)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define int long long
#define jiaa(a,b) {a+=b;if(a>=MOD) a-=MOD;}
#define jian(a,b) {a-=b;if(a<0) a+=MOD;}
using namespace std;
int ksm(int a,int b,int p){
	if(b==0) return 1;
	if(b==1) return a%p;
	int c=ksm(a,b/2,p);
	c=c*c%p;
	if(b%2==1) c=c*a%p;
	return c%p;	
}
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int fa[1000005],maxx[1000005],sec[1000005],dp[1000005];
bool dfs(int x){
	int ff=fa[x];
	if(dp[x]>maxx[ff]){
		sec[ff]=maxx[ff];
		maxx[ff]=dp[x];
	}
	else if(dp[x]>sec[ff]){
		sec[ff]=dp[x];
	}
	int ans=max(maxx[ff],sec[ff]+1);
	if(ans==dp[ff]) return 0;
	dp[ff]=ans;
	return 1;
}
signed main()
{
	//freopen("filename.in", "r", stdin);
	//freopen("filename.out", "w", stdout);
	int n=read();
	fa[1]=0;
	dp[1]=1;
	for(int i=1;i<=n;i++){
		fa[i+1]=read();
		int j=i+1;
		dp[j]=1;
		while(fa[j]){
			if(!dfs(j)) break;
			j=fa[j];
		}
		cout<<maxx[1]<<'\n';
	}
	return 0;
}

T4 [USACO23FEB] Watching Cowflix P

考虑暴力DP

设 fi,0/1 表示 i 为根的子树,i 现在不在/在连通块内的最小代价

其中关键点必须在连通块里

转移时考虑节点u是否选

发现若两个联通块之间的点的个数<=k,则合并不劣

于是发现联通快个数m<=n/k

若k<=sqrt(n),直接暴力

否则,块个数<=sqrt(n)

设dpi,j,0/1表示 i 为根的子树,分成 j 个连通块,其中 i 不在/在连通块里的最小代价

转移是树上背包

T5 [IOI 2014] friend 朋友

分情况

设fi,0/1表示i等效于选or不选

case1:没有上司的舞会

case2:发现这种情况i点和fa_i的影响是一样的

image

case3:发现这只是将1~2情况合并

#include<bits/stdc++.h>
using namespace std;
int dp[100005][2];
int findSample(int n, int confidence[], int host[], int protocol[]){
	for(int i=0;i<n;i++) dp[i][1]=confidence[i];
	for(int i=n-1;i;i--){
		if(!protocol[i]){
			dp[host[i]][0]+=max(dp[i][0],dp[i][1]);
			dp[host[i]][1]+=dp[i][0];
		}
		else if(protocol[i]==1){
			dp[host[i]][1]=max(dp[host[i]][1]+max(dp[i][0],dp[i][1]),dp[host[i]][0]+max(dp[i][0],dp[i][1]));
			dp[host[i]][0]+=dp[i][0];
		}
		else{
			dp[host[i]][1]=max(dp[host[i]][1]+dp[i][0],dp[host[i]][0]+max(dp[i][0],dp[i][1]));
			dp[host[i]][0]+=dp[i][0];
		}
	}
	return max(dp[0][0],dp[0][1]);
}

T6 [ARC171D] Rolling Hash

考虑hash的本质

枚举子集 O(n^3)

证明

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define int long long
#define jiaa(a,b) {a+=b;if(a>=MOD) a-=MOD;}
#define jian(a,b) {a-=b;if(a<0) a+=MOD;}
using namespace std;
int ksm(int a,int b,int p){
	if(b==0) return 1;
	if(b==1) return a%p;
	int c=ksm(a,b/2,p);
	c=c*c%p;
	if(b%2==1) c=c*a%p;
	return c%p;
}
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int tu[20][20],f[1000005],dp[1000005];
signed main()
{
	//freopen("filename.in", "r", stdin);
	//freopen("filename.out", "w", stdout);
	int P=read(),b=read(),n=read(),m=read();
	for(int i=1;i<=m;i++){
		int l=read(),r=read();
		tu[l-1][r]=tu[r][l-1]=1;
	}
	if(P>=n){
		cout<<"Yes"<<'\n';
		return 0;
	}
	for(int i=0;i<(1<<(n+1));i++){
		f[i]=1;
		for(int u=0;u<=n;u++){
			if(i>>u&1){
				for(int v=u+1;v<=n;v++){
					if((i>>v&1)&&tu[u][v]){
						f[i]=0;
						break;
					} 
				}
				if(!f[i]) break;
			}
		}
	}
	memset(dp,0x3f,sizeof(dp));
	dp[0]=0;
	for(int i=1;i<(1<<(n+1));i++){
		for(int t=i;t;t=(t-1)&i){
			if(f[t]) dp[i]=min(dp[i],dp[t^i]+1);
		}
	}
	if(dp[(1<<(n+1))-1]<=P) cout<<"Yes"<<'\n';
	else cout<<"No"<<'\n';
	return 0;
}

T7 [省选联考 2021 A/B 卷] 滚榜

考虑暴力状压

设 fS,i,j,k 表示前 |S| 个位置放了集合 S,上一个放的 i,上一个 b 为 j,之前 b 总和为 k 的方案数

但复杂度爆炸

考虑将b筛掉

发现原来限制是:

bi+1>=bi

b_i+1 + a_i+1>=bi + ai

将其移项

变成 b_i+1 - bi >=0

b_i+1 - bi + a_i+1>=ai

发现记录差分数组即可删掉b这一维

于是设 fS,i,j 表示集合 S 已经放入,上一个放的 i,前面的 b 和这些 b 对后面的影响一共占用了 j 的方案数

转移即可

T8 [NOI2015] 寿司晚宴

因为一个数大于根号的质因子最多只有一个

故对质因子进行状压

若k<sqrt(n)直接DP给二人或都不给

否则将大质数的倍数全部只能给某一个人

复杂度 O(3^k*n),其中 k 是取的小质数数量,这里为 8

T9 [NOIP 2017 提高组] 宝藏

树形DP/按层DP + 状压DP

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=5005;
int f[maxn][20][20];
int tu[20][20];
int n,m;
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin>>n>>m;
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			tu[i][j]=1000000000;
		}
	}
	for(int S=0;S<maxn;S++){
		for(int i=0;i<n;i++){
			for(int j=1;j<=n+1;j++){
				f[S][i][j]=1000000000;
			}
		}
	}
	for(int i=1;i<=m;i++){
		int u,v,w;
		cin>>u>>v>>w;
		tu[u-1][v-1]=tu[v-1][u-1]=min(tu[u-1][v-1],w);
	}	
	for(int i=0;i<n;i++){
		for(int j=1;j<=n;j++){
			f[1<<i][i][j]=0;
		}
	}
	for(int S=0;S<(1<<n);S++){
		for(int i=0;i<n;i++){
			if(!((S>>i)&1)) continue;
			for(int j=1;j<=n;j++){
				for(int k=0;k<n;k++){
					for(int T=S;T;T=(T-1)&S){
						if(!((T>>k&1))) continue;
						f[S][i][j]=min(f[S][i][j],f[S^T][i][j]+f[T][k][j+1]+tu[i][k]*j);
					}
				}
			}
		}
	}
	int ans=1000000000;
	for(int i=0;i<n;i++){
		for(int j=1;j<=n;j++){
			ans=min(ans,f[(1<<n)-1][i][j]);
		}
	}
	cout<<ans<<'\n';
	return 0;
}

T10 [JLOI2015] 管道连接

最小斯坦纳树

考虑对最小斯坦纳树再进行状压

posted @ 2025-08-12 08:03  gbrrain  阅读(6)  评论(0)    收藏  举报