水一篇打卡题的博客
1600 - C. Hard problem
题目链接:https://codeforces.com/problemset/problem/706/C
题意:
给你长度为 N 的字符串数组,每个字符串可以进行翻转,翻转的代价为 Ci
问要使字符串数组从 1~N 按字典序从小到大的最小代价为多少
分析:
简单 dp 。
先转换一下题意:
有一个长度为 n 的数组 A , 你可以花费 Ci 的代价使得 Ai → Bi
现你要用最小的代价让数组从小到大不降序
(其中 Ai 为翻转前的字符串大小 , Bi 为翻转后的字符串大小)
考虑 dp[i][0] 表示前 i 个数已排好序,第 i 个选择 Ai 的代价
dp[i][1] 表示前 i 个数已排好序,第 i 个选择 Bi 的代价
那么在都合法的情况下 dp[i][0] = min(dp[i - 1][0] , dp[i - 1][1])
dp[i][1] = min(dp[i - 1][0] + c[i] , dp[i - 1][1] + c[i])
不都合法的情况下删去几个转移即可
最后答案为 min ( dp[n][0] , dp[n][1] )
#include<bits/stdc++.h> #define rep(i,a,n) for (int i=a;i<=n;i++) #define int long long using namespace std; const int INF (0x3f3f3f3f3f3f3f3fll); const int N = 3e5 + 10; int c[N] , dp[N][2]; string s[N] , t[N]; signed main() { rep(i , 0 , N - 1) dp[i][0] = dp[i][1] = INF; int n ; cin >> n; rep(i , 1 , n) cin >> c[i]; rep(i , 1 , n) cin >> t[i] , s[i] = t[i] , reverse(t[i].begin() , t[i].end()); dp[1][0] = 0 , dp[1][1] = c[1]; rep(i , 2 , n) { if(s[i] >= s[i - 1]) dp[i][0] = dp[i - 1][0]; if(s[i] >= t[i - 1]) dp[i][0] = min(dp[i][0] , dp[i - 1][1]); if(t[i] >= s[i - 1]) dp[i][1] = dp[i - 1][0] + c[i]; if(t[i] >= t[i - 1]) dp[i][1] = min(dp[i][1] , dp[i - 1][1] + c[i]); } int ans = min(dp[n][0] , dp[n][1]); if(ans == INF) cout << -1 << ' '; else cout << ans << ' '; return 0; }
1800 - H. Bots
题目链接:https://codeforces.com/contest/575/problem/H
题意:
你起初处于坐标系的 (0 , 0) 位置。现给你一个 N
表示你可以活动的范围为 (0 , 0) 到 (N , N) 所形成的矩形之内
每步你可以选择向上或者向右走即 (x , y) → (x + 1 , y) 或 (x , y + 1)
问你到达矩形内的所有点的所有方案总和为多少
分析:
我们定义 dp[i][j] 表示从 (0 , 0) 到 (i , j) 的方案数
则 $ans=sum ^{n}_{i=0}sum ^{n}_{j=0}dpleft[ i ight] left[ j ight] $
因为从 (0 , 0) → (i , j) 一共要走 i + j 步,而其中有 i 步向上 , j 步向右
所以一共有 $C^{i}_{i+j}$ 步 , 即 dp[i][j] = $C^{i}_{i+j}$
于是 $ans=sum ^{n}_{i=0}sum ^{n}_{j=0}C^{i}_{i+j}$
又因为 $C^{b}_{a}+C^{b+1}_{a}=C^{b+1}_{a+1}$ , 所以式子可以化简为 $C^{n+1}_{2n+2}-1$
(手写 latex 太累了,偷个懒 ( ´・∀・`))
注意模数太大不能用lucas等 , 所以还是乖乖的算阶层吧
#include<bits/stdc++.h> #define rep(i,a,n) for (int i=a;i<=n;i++) #define per(i,n,a) for (int i=n;i>=a;i--) #define int long long using namespace std; const int N = 3e5 + 10 , MOD = 1e9 + 7; int pow_mod(int x , int n , int mod) { int res = 1; while(n) { if(n & 1) res = res * x % mod; x = x * x % mod , n >>= 1; } return res; } int dp[N][2]; signed main() { int n , res = 1; cin >> n; per(i , 2 * n + 2 , 2 * n + 2 - n) res *= i , res %= MOD; rep(i , 2 , n + 1) res *= pow_mod(i , MOD - 2 , MOD) , res %= MOD; cout << (res - 1 + MOD) % MOD << ' '; return 0; }
2000 - C. Industrial Nim
题目链接:https://codeforces.com/contest/15/problem/C
题意:
现在有 N 个矿场 , 第 i 个矿场有 Mi 辆货车
每辆货车上的物品个数依次为 Xi , Xi + 1 ... Xi + M - 1
现有两人轮流取物
每次可以选择从任意矿场的任意一辆车取上 (1 ~ 该车的物品总量 ) 件物品
当一方无法再取物时游戏结束,问先手赢还是后手赢
分析:
很显然这就是道赤裸裸的 NIM 博弈题 , 不懂为什么有会2000分?
如果你不了解 NIM 博弈,推荐学习博客 博弈论
于是按照 NIM 博弈的结论我们只要判断所有车的异或总和是否为0就可以了
问题是 M 的范围很大,暴力异或肯定分分钟 Tle,那怎么办呢?
其实也很简单 , 我们会发现一个矿场的货车物品数是以 1 为公差递增的
而如果一个偶数 EV ^ (EV + 1) , 很显然结果为 1
所以我们只要对 Xi 和 Mi 的奇偶性进行讨论就可以了
具体操作还是看代码吧 (请原谅我喜欢偷懒(′ε`")
#include<bits/stdc++.h> #define rep(i,a,n) for (int i=a;i<=n;i++) #define int long long using namespace std; signed main() { int n , m , x , ans = 0; cin >> n; rep(i , 1 , n) { cin >> x >> m; if(x & 1) { ans ^= x; if((m - 1) & 1) ans ^= x + m - 1; int len = m - 1 >> 1; if(len & 1) ans ^= 1; } else { if(m & 1) ans ^= x + m - 1; int len = m >> 1; if(len & 1) ans ^= 1; } } if(ans) cout << "tolik "; else cout << "bolik "; return 0; }