20261003 DP专题

A. 乐乐

一个 \(2\) x \(n\) 的网格,每个格子可以放置豹子或者牛。四联通相邻(即上下左右)的同种动物会一起乐乐,并且乐乐具有传递性。

求恰好最多有 \(k\) 个格子的动物互相不乐乐的方案数。答案对 \(998244353\) 取模。

  • 首先由最多很容易注意到网格中的动物恰好构成 \(k\) 个连通块,同一个块中出现两个格子它们就会一起乐乐,故每个块贡献一个不乐乐的格子;

  • 现在考虑如何求方案数。设 \(dp[i][j][1/2][1/2]\) 表示第 \(i\) 列,前 \(1\) ~ \(i\) 列共有 \(j\) 个连通块,第 \(i\) 列的上面放豹子/牛、下面放豹子/牛的方案数;

  • 如何转移:枚举即可。

#include<bits/stdc++.h>
using namespace std;
long long g[1003][3][3][2003];
const long long mod=998244353;
int main(){
	freopen("A.in","r",stdin);
	freopen("A.out","w",stdout);
	int n,k;
	scanf("%d%d",&n,&k);
	memset(g,0,sizeof(g));
	g[1][1][1][1]=g[1][2][2][1]=g[1][1][2][2]=g[1][2][1][2]=1;
	for(int i=2;i<=n;i++){
		g[i][1][1][1]=g[i-1][1][1][1];
		g[i][2][2][1]=g[i-1][2][2][1];
		g[i][1][1][2]=(g[i-1][1][2][2]+g[i-1][2][1][2]+g[i-1][1][1][2]+g[i-1][2][2][1])%mod;
		g[i][2][2][2]=(g[i-1][1][2][2]+g[i-1][2][1][2]+g[i-1][1][1][1]+g[i-1][2][2][2])%mod;
		g[i][1][2][2]=(g[i-1][1][1][1]+g[i-1][2][2][1]+g[i-1][1][2][2])%mod;
		g[i][2][1][2]=(g[i-1][1][1][1]+g[i-1][2][2][1]+g[i-1][2][1][2])%mod;
		for(int t=3;t<=2*i;t++){
			g[i][1][1][t]=(g[i-1][1][2][t]+g[i-1][2][1][t]+g[i-1][1][1][t]+g[i-1][2][2][t-1])%mod;
			g[i][1][2][t]=(g[i-1][1][1][t-1]+g[i-1][1][2][t]+g[i-1][2][1][t-2]+g[i-1][2][2][t-1])%mod;
			g[i][2][1][t]=(g[i-1][1][1][t-1]+g[i-1][1][2][t-2]+g[i-1][2][1][t]+g[i-1][2][2][t-1])%mod;
			g[i][2][2][t]=(g[i-1][1][2][t]+g[i-1][2][1][t]+g[i-1][2][2][t]+g[i-1][1][1][t-1])%mod;
		}
	}
	printf("%lld\n",(g[n][1][1][k]+g[n][1][2][k]+g[n][2][1][k]+g[n][2][2][k])%mod);
	return 0;
}

B. 于老

一棵无根树,共有 \(2\) x \(k\) 个叶子节点,所有节点均可染成红色或黑色。

定义树的价值为两端点颜色相同的边数。求恰好 \(k\) 个叶子节点染成黑色、余下 \(k\) 个叶子节点染成红色的最大价值。

\(std\) 代码如下:

#include<bits/stdc++.h>
#define N 5005
#define inf 1000000000
using namespace std;
vector<int> e[N];
int f[N][N][2],in[N],sz[N],tmp[N][2];
bool vis[N];
void dfs(int u,int fa){
	for(int i=0;i<N;i++)f[u][i][0]=f[u][i][1]=inf;
	if(vis[u]){
		sz[u]=1;
		f[u][1][1]=0;
		f[u][0][0]=0;
		return;
	}
	f[u][0][1]=0;
	f[u][0][0]=0;
	sz[u]=1;
	for(int v:e[u]){
		if(v==fa)continue;
		dfs(v,u);
		for(int i=0;i<=sz[u]+sz[v];i++)tmp[i][0]=tmp[i][1]=inf;
		for(int k=0;k<=sz[u];k++){
			for(int j=0;j<=sz[v];j++){
				tmp[k+j][0]=min(tmp[k+j][0],f[u][k][0]+f[v][j][0]);
				tmp[k+j][0]=min(tmp[k+j][0],f[u][k][0]+f[v][j][1]+1);
				tmp[k+j][1]=min(tmp[k+j][1],f[u][k][1]+f[v][j][1]);
				tmp[k+j][1]=min(tmp[k+j][1],f[u][k][1]+f[v][j][0]+1);
			}
		}
		sz[u]+=sz[v];
		for(int i=0;i<=sz[u];i++)f[u][i][0]=tmp[i][0],f[u][i][1]=tmp[i][1];
	}
}
int main(){
	freopen("B.in","r",stdin);
	freopen("B.out","w",stdout);
	int n;
	cin>>n;
	for(int i=1;i<n;i++){
		int u,v;
		cin>>u>>v;
		e[u].push_back(v);
		e[v].push_back(u);
		in[u]++,in[v]++; 
	}
	int cnt=0;
	for(int i=1;i<=n;i++){
		if(in[i]==1){
			vis[i]=1;
			cnt++; 
		} 
	}
	int root=1;
	while(vis[root])root++;
	if(root>n)root=1;
	dfs(root,0);
	if(n==2)cout<<"0";
	else  cout<<n-1-min(f[root][cnt/2][0],f[root][cnt/2][1]);
	return 0;
}

C. 牛来

\(n\) 堆草,第 \(i\) 堆重 \(a_i\)。每次操作可以选择一堆两边都不为空的草堆 \(i\),吃掉并将两边的草堆分别加上 \(a_i\)。

求最终剩下两堆草的最小总重量。

  • 首先联想到典中典 · 石子合并,容易知道区间 \(dp\);

  • 对于区间 \(i\) ~ \(j\),每次枚举一个断点 \(k\),\(dfs\) 的求解左右两个区间 \(i\) ~ \(k-1\) 和 \(k+1\) ~ \(j\),返回两端点的可能结果组合(因为重量和最小也有可能有不同的组合情况),用 \(vector\) 之类的东西记录,枚举两边区间的搭配,更新最小值;

  • 对于上述的两端点的可能结果组合,在存的时候就把一些显然不可能创造出最终结果的搭配优化掉。又因为 \(dfs\) 会有之前搜过的情况,所以记忆化一下即可;

D. 牛不来

有 \(m\) 条弦,一条弦可以抽象为一个二元组 \((u,v)\),你可以进行任意次切割操作,一次切割操作你将选择两个下标 \(i\) 和 \(j\) 满足 \(i,j \in [1,n]\),然后所有满足 \(u>i,v<j\) 的弦 \((u,v)\) 都将被破坏,同时你将付出 \(a_i \times b_j​\) 的代价。求破坏所有弦的最小代价和。

  • 显然可以将 \((u,v)\) 看成二维平面上的点,然后切割弦的操作可以看成是一个点 \((i,j)\),将它右下的部分所覆盖的点全部删掉;

  • 然后显然只有最外围的点有用,内部的点都是滚木;

  • 对于剩下来的似凸包又不似凸包的东西,可以考虑设 \(dp[i]\) 表示当前处理到第 \(i\) 个点,且前 \(1\) ~ \(i\) 的点都已经被覆盖并删除。那么可以枚举前一个点 \(j\),\(dp[i]=dp[j]+solve(i)\);

然后据讲题人所述,\(solve(i)\) 就是斜率优化的板子。

E. 数学思维

一个 \(0\) ~ \(m\) 的数轴,其中有 \(n\) 个摊子,每个摊子产出同人都需要 \(t\) 的时间。也就是说第一次抵达某个摊子后,过 \(t\) 时刻再回来,就可以得到一份同人作品。

现有一人从 \(0\) 开始走,需要在 \(m\) 处离开,求获得所有同人的最短时间。

省流:我不会。

只能参考这个了(悲)。

posted @ 2026-10-03 16:06  404-NoT_FinD  阅读(1)  评论(0)    收藏  举报