今天心态崩崩,,,.。
这道题比较简单 因为每一位是单独对答案产生贡献的 所以枚举每一位 用数位dp求出该位是$1$的数量 在求出该位是$0$的
那么答案就是那一位对应的二的幂次再乘以$num1 * num0 * 2$ 每一对会产生两次贡献
代码
#include <bits/stdc++.h> #define rg register #define il inline using namespace std; typedef long long ll; const ll MOD = 1e9 + 7; int T, dig[40], lim; ll p[50], dp[40][2][2], L, R, ans; il ll read( ) { ll t = 1, ans = 0; char x; x = getchar( ); while(x < '0' || x > '9') { if(x == '-') t = -1; x = getchar( ); } while(x >= '0' && x <= '9') { ans = ans * 10 + x - '0'; x = getchar( ); } return ans * t; } il void Init( ) { p[0] = 1; for(rg int i = 1;i <= 32;i ++) { p[i] = p[i - 1] * 2 % MOD; } } il void init(ll a) { ll b = a; lim = 0; while(b) { dig[++ lim] = b & 1; b >>= 1; } } il ll dfs(int dep, int up, ll ans, int pos) { if(dep == 0) return ans; if(~ dp[dep][up][ans]) return dp[dep][up][ans]; int h = up ? dig[dep] : 1; ll tmp = 0; for(rg int i = 0;i <= h;i ++) { if((dep == pos) && (i == 1)) tmp += dfs(dep - 1, up && (i == h), ans + 1, pos); else tmp += dfs(dep - 1, up && (i == h), ans, pos); } return dp[dep][up][ans] = tmp; } il ll solve(ll a, ll pos) { init(a); memset(dp, -1, sizeof(dp)); return dfs(lim, 1, 0, pos); } il void Solve( ) { scanf("%d",& T); while(T --) { ll L, R, l; L = read( ), R = read( ); l = L; if(L == 0) l = 1; ans = 0; for(rg int i = 0;i <= 31;i ++) { ll num1 = solve(R, i + 1) - solve(l - 1, i + 1); ll num0 = (R - L + 1 - num1) % MOD; ll num = 2 * num1 % MOD * num0 % MOD; ans = (ans + p[i] * num % MOD) % MOD; } printf("%lld ", ans); } } int main( ) { freopen("xor.in","r",stdin); freopen("xor.out","w",stdout); Init( ); Solve( ); }
这道题是一道博弈论的dp 现在用$dp[x][y][z]$数组表示三堆分别是$x,y,z$时他必输还是必胜
我们可以通过已经求出来的必败态来筛掉必败态 因为若是后继状态中有必败态 那么他就是必胜态 否则是必败态
若一个确定了一堆的状态为必败态 那么若剩下两堆的差值是固定的 它就可以通过加减到达我现在的状态 那么我现在状态就必胜
同样的 若我确定了两堆 那么另外一堆不管是什么都可以直接必胜
最后是三堆 若三堆确定了 那么它们两两差值也就确定了 同样可以确定我现在的状态
代码
#include <bits/stdc++.h> using namespace std; const int N = 305; int T, p[5]; bool dp[N][N][N], h[N][N][6]; void Solve( ) { scanf("%d",& T); for(int i = 0;i <= 300;i ++) for(int j = 0;j <= i;j ++) { for(int k = 0;k <= j;k ++) { bool tag = true; if(h[i][j - k][0]) tag = false; if(h[j][i - k][0]) tag = false; if(h[k][i - j][0]) tag = false; if(h[i][j][1]) tag = false; if(h[j][k][1]) tag = false; if(h[i][k][1]) tag = false; if(h[i - j][j - k][2]) tag = false; if(tag) { dp[i][j][k] = true; h[i][j - k][0] = h[j][i - k][0] = h[k][i - j][0] = h[i][j][1] = h[j][k][1] = h[i][k][1] = h[i - j][j - k][2] = true; } } } } int main( ) { freopen("stone.in","r",stdin); freopen("stone.out","w",stdout); Solve( ); while(T --) { scanf("%d%d%d",& p[0],& p[1],& p[2]); sort(p, p + 3); if(dp[p[2]][p[1]][p[0]]) printf("No "); else printf("Yes "); } }
这道题是一道恶星$dp$呕 这道题首先可以确定这玩意贪心取每一个值
然后$dp[i][j][k]$表示我到了第$i$个数 我总共分了$j$段当前状态是$0/1/2/3$时候的最优答案
状态是什么呢 我们考虑拆开绝对值 那么一段数$S$产生的贡献是他的两倍 不产生贡献 或者产生负二倍贡献
所以贡献就是$2 0 0 -2$ 这种 并且不可能出现$2 0 0 0 2$这种 因为若他是$2$那么就需要负的去补 然后后面跟着一堆$0$也就是说最后两个盛了两个正的 所以末尾肯定是两个负的去补
状态$0$表示当前这个点在贡献为正的一段 $1$表示贡献为$0$的一段 并且这一段是跟在贡献为正的后面 $2/3$跟$1$正好相反
转移 $dp[i][j][0]$由 $dp[i - 1][j][0]/dp[i - 1][j - 1][2/3]$转移来 其余的类似 记住特判第一个和最后一个只产生$1 / -1$的贡献
代码
#include <bits/stdc++.h> using namespace std; const int N = 30004; int dp[N][205][5], a[N], n, k; void Init( ) { scanf("%d%d",& n,& k); for(int i = 1;i <= n;i ++) scanf("%d",& a[i]); } void Solve( ) { memset(dp, 128, sizeof(dp)); for(int i = 0;i <= n;i ++) dp[i][0][0] = dp[i][0][2] = 0; for(int i = 1;i <= n;i ++) { for(int j = 1;j <= min(i, k); j ++) { int flag = 2 - (j == 1 || j == k); dp[i][j][0] = max(dp[i - 1][j - 1][3], max(dp[i - 1][j][0], dp[i - 1][j - 1][2])) + flag * a[i]; if(j > 1 && j < k) dp[i][j][1] = max(dp[i][j][1], dp[i - 1][j][0]); dp[i][j][2] = max(dp[i - 1][j - 1][1], max(dp[i - 1][j][2], dp[i - 1][j - 1][0])) - flag * a[i]; if(j > 1 && j < k) dp[i][j][3] = max(dp[i - 1][j][3], dp[i - 1][j - 1][2]); } } int ans = 0; for(int i = k;i <= n;i ++) ans = max(ans, max(dp[i][k][2], dp[i][k][0])); printf("%d ", ans); } int main( ) { freopen("optimization.in","r",stdin); freopen("optimization.out","w",stdout); Init( ); Solve( ); }