水一篇打卡题的博客
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 << '\n';
else cout << ans << '\n';
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}dp\left[ i\right] \left[ j\right] $
因为从 (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 << '\n';
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\n";
else cout << "bolik\n";
return 0;
}