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;
}
大地の閾を探して
建议直接拉到最下面看题意
本质上是两个题:
- 从树上选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;
}