Codeforces 每日一练 706C+575H+15C

欢迎进群互% :1042591643

706C Hard problem

传送门
镜像
题意:给定n个字符串,反转每个字符串有代价c[i],求使得所有字符串按字典序排列所需的最小代价,如果不可能,输出-1;
看一眼数据范围和题意大概就猜出来是用二维了,0代表不反转,1代表反转,转移方程也很容易就可以写出来

dp[i][1]=min(dp[i-1][0]+c[i],dp[i-1][1]+c[i]);
dp[i][0]=min(dp[i-1][0],dp[i-1][1]);        

如果该情况合法就去转移,但是要注意,如果某种情况不合法,那么该情况就不能用于后面的转移了,因此我开了另一个数组去记录是否合法,同时记录是否发生了转移,如果没有,说明无法满足题意,直接输出-1;

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define maxn 100005
#define eps 1e-4
#define IOS ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#pragma GCC optimize(2)
#define lowbit(x) ((x)&-(x))
#define ull unsigned long long
#define db double
#define ld long double
#define mod 1000000007
string s[maxn],t[maxn];
ll c[maxn];
ll dp[maxn][2];
int vis[maxn][2];
signed main(){
    IOS
    int n;
    cin>>n;
    for (int i = 1; i <=n ; ++i) {
        cin>>c[i];
        dp[i][0]=dp[i][1]=9223372036854775800;
    }
    for (int i = 1; i <=n ; ++i) {
        cin>>s[i];
        reverse(s[i].begin(),s[i].end());
        t[i]=s[i];
        reverse(s[i].begin(),s[i].end());
    }
    dp[1][0]=0;
    dp[1][1]=c[1];
    vis[1][1]=1;
    vis[1][0]=1;
    for (int i = 2; i <=n ; ++i) {
        int fl=0;
        if(t[i]>=s[i-1]&&vis[i-1][0]==1){
            vis[i][1]=1;
            dp[i][1]=min(dp[i][1],dp[i-1][0]+c[i]);
            fl=1;
        }
        if(t[i]>=t[i-1]&&vis[i-1][1]==1){
            vis[i][1]=1;
            dp[i][1]=min(dp[i][1],dp[i-1][1]+c[i]);
            fl=1;
        }
        if(s[i]>=s[i-1]&&vis[i-1][0]==1){
            vis[i][0]=1;
            dp[i][0]=min(dp[i][0],dp[i-1][0]);
            fl=1;
        }
        if(s[i]>=t[i-1]&&vis[i-1][1]==1){
            vis[i][0]=1;
            dp[i][0]=min(dp[i][0],dp[i-1][1]);
            fl=1;
        }
        if(!fl){
            cout<<"-1";
            return 0;
        }
    }
    cout<<min(dp[n][1],dp[n][0]);
    return 0;
}

575H Bots

传送门
镜像
题意:两个机器人分别可以走0-n步,且分别会把走过的步数染成红色或蓝色,求所有的染色情况数。
想出答案并不难,明显答案应该是i=0nj=0nCi+jisum_{i=0}^nsum_{j=0}^nC_{i+j}^{i},难点在化简求值,首先关于组合数有恒等式Cn+1m+1=Cnm+1+Cnm C_{n+1}^{m+1}=C_{n}^{m+1}+C_{n}^{m}
根据这个恒等式可以将第一个求和化简为Ci+n+1i+1C_{i+n+1}^{i+1},之后在化简第二个求和时,先加上Cn+21C_{n+2}^{1},就可以把答案化简为C2n+2n+1+Cn+11C_{2*n+2}^{n+1}+C_{n+1}^{1},之后减去Cn+21C_{n+2}^{1},则答案即为C2n+2n+11C_{2*n+2}^{n+1}-1的值取模。
卢卡斯定理套个板子就OK

#include <bits/stdc++.h>
#define mod 1000000007
using namespace std;
typedef long long ll;
ll mulit(ll a,ll b,ll m){
    ll ans=0;
    while(b){
        if(b&1) ans=(ans+a)%m;
        a=(a<<1)%m;
        b>>=1;
    }
    return ans;
}
ll quick_mod(ll a,ll b,ll m){
    ll ans=1;
    while(b){
        if(b&1){
            ans=mulit(ans,a,m);
        }
        a=mulit(a,a,m);
        b>>=1;
    }
    return ans;
}
ll comp(ll a,ll b,ll m){
    if(a<b) return 0;
    if(a==b) return 1;
    if(b>a-b) b=a-b;
    ll ans=1,ca=1,cb=1;
    for(int i=0;i<b;i++){
        ca=ca*(a-i)%m;
        cb=cb*(b-i)%m;
    }
    ans=ca*quick_mod(cb,m-2,m)%m;
    return ans;
}
ll lucas(ll a,ll b,ll m){
    ll ans=1;
    while(a&&b){
        ans=(ans*comp(a%m,b%m,m))%m;
        a/=m;
        b/=m;
    }
    return ans;
}
int main()
{
    ll a,b,m,n;
    cin>>n;
    ll ans=lucas(2*n+2,n+1,mod)-1;
    ans%=mod;
    cout<<ans;
    return 0;
}

15C Industrial Nim

直接贴大佬的题解啦
写的比我好多了Orz
传送门

原文地址:https://www.cnblogs.com/Bazoka13/p/12635505.html