U197241 ご注文はうさぎですか?和 U197124 大地の閾を探して

ご注文はうさぎですか?

HINT:
总和对7取模只有7种状态,是不是可以把每个生命值下能取到的最大总和记录一下?

Solve:
因为总和对7取模只有7个状态,生命值最大1000,所以dp的状态可以第一维记录这个状态对7取模的值,dp数组本身记录当前状态能取到的最大总和

所以先枚举物品i,再枚举能量h,最后枚举从哪个已存在的总和进行转移
即:

dp[i][(j+a[i])%7][h] = max(dp[i][(j+a[i])%7][h], 
			   dp[i-1][j%7][h - b[i]] + a[i])

可以省略第一维,转移的时候额外开一个tmp记录一下上一个物品的状态即可

#include<bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
#define pll pair<ll,ll>
#define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
using namespace std;

const int maxn = 1e5 + 5;
const int inf = 1e9 + 7;
const ll lnf = 1e18;
const ll mod = 998244353;

int a[maxn], b[maxn];

ll dp[7][1005];
ll tmp[7][1005];

int main()
{
	fastio;
	int n, h;
	cin >> n >> h;
	for (int i = 1; i <= n; i++)
		cin >> a[i];
	for (int i = 1; i <= n; i++)
		cin >> b[i];
	memset(dp, -1, sizeof(dp));
	memset(tmp, -1, sizeof(tmp));
	dp[0][0] = 0;
	tmp[0][0] = 0;
	for (int i = 1; i <= n; i++) {

		for (int j = 0; j < 7; j++) {
			for (int k = 0; k <= h; k++) {
				if (k + b[i] > h)break;
				if (dp[j][k] == -1)continue;
				tmp[(j + a[i]) % 7][k + b[i]] = max(tmp[(j + a[i]) % 7][k + b[i]], dp[j][k] + a[i]);
			}
		}

		for (int j = 0; j < 7; j++) {
			for (int k = 0; k <= h; k++) {
				dp[j][k] = tmp[j][k];
			}
		}
	}
	ll ans = 0;
	for (int k = 0; k <= h; k++)
		ans = max(ans, dp[0][k]);
	cout << ans;
	return 0;

}

大地の閾を探して

建议直接拉到最下面看题意

image

本质上是两个题:

  • 从树上选m条边,边与边之间没有公共点的方案数
  • 剩下2n-2m个点两两自由组合的方案数

这两个贡献乘在一起就可以了

  • 对于第一个题(建议直接看代码):
    树上背包,记录每个点是否选取子树临边的状态下,以它为根的子树选了x条边的方案数

    dp数组记录的是对于父节点from枚举完to之前所有子树的方案数,现在需要将to带来的贡献转移到答案中,需要临时数组g记录转移后的结果(直接在dp上加的话会导致子树to自己的贡献相互作用),在将g复制给dp。

    // from和to都没选
    g[i + j][0] += dp[from][i][0] * (dp[to][j][0] + dp[to][j][1]) % mod;
    g[i + j][0] %= mod;
    
    // from在枚举to之前已经选了
    g[i + j][1] += dp[from][i][1] * (dp[to][j][0] + dp[to][j][1]) % mod;
    g[i + j][1] %= mod;
    
    // 选from和to相连的这条边
    g[i + j + 1][1] += dp[from][i][0] * dp[to][j][0] % mod;
    g[i + j + 1][1] %= mod;
    
  • 第二个题:
    递推,有n-1个数,多加入1个数,这个数和前面所有数配对(n-1),剩下的数两两配对(f[n-2])
    即:$f_n =(n-1) \cdot f_{n-2} $

#include<bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
#define pll pair<ll,ll>
#define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
using namespace std;

const int maxn = 3e5 + 5;
const int inf = 1e9 + 7;
const ll lnf = 1e18;
const ll mod = 998244353;

vector<int>G[maxn];

ll dp[4005][2005][2];
ll g[2005][2];
int siz[maxn];

void dfs(int from, int fa) {
	siz[from] = 1;
	dp[from][0][0] = 1;
	for (auto to : G[from]) {
		if (to == fa)continue;
		dfs(to, from);
		for (int i = 0; i <= (siz[from]+ siz[to]) / 2; i++)
			g[i][0] = 0, g[i][1] = 0;
		for (int i = 0; i <= siz[from] / 2; i++)
			for (int j = 0; j <= siz[to] / 2; j++) {
				g[i + j][0] += dp[from][i][0] * (dp[to][j][0] + dp[to][j][1]) % mod;
				g[i + j][0] %= mod;
				g[i + j][1] += dp[from][i][1] * (dp[to][j][0] + dp[to][j][1]) % mod;
				g[i + j][1] %= mod;
				g[i + j + 1][1] += dp[from][i][0] * dp[to][j][0] % mod;
				g[i + j + 1][1] %= mod;
			}
		siz[from] += siz[to];
		for (int i = 0; i <= siz[from] / 2; i++)
			dp[from][i][0] = g[i][0], dp[from][i][1] = g[i][1];
		/*cout << from << ":" << endl;
		for (int i = 0; i <= siz[from] / 2; i++)
			cout << dp[from][i][0] << " ";
		cout << endl;
		for (int i = 0; i <= siz[from] / 2; i++)
			cout << dp[from][i][1] << " ";
		cout << endl << endl;*/

	}

}

ll f[maxn];

int main()
{
	fastio;
	int n, m;
	cin >> n >> m;
	for (int i = 1; i < 2 * n; i++) {
		int x, y;
		cin >> x >> y;
		G[x].push_back(y);
		G[y].push_back(x);
	}
	dfs(1, 0);
	f[1] = 1;
	f[2] = 1;
	for (int i = 3; i <= 2 * n; i++)
		f[i] = f[i - 2] * (i - 1) % mod;
	cout << (dp[1][m][0] + dp[1][m][1]) * f[2 * n - 2 * m] % mod;

	return 0;

}
posted @ 2022-05-02 18:31  Lecoww  阅读(30)  评论(0编辑  收藏  举报