Codeforces Fifty-Seven

水一篇打卡题的博客 

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 的代价

扫描二维码关注公众号,回复: 10406746 查看本文章

  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;
}

猜你喜欢

转载自www.cnblogs.com/StarRoadTang/p/12618844.html