2019 Multi-University Training Contest 7

1001 A + B = C

题目链接
题意:给出a,b,c,寻找x,y,z满足式子
思路:将a,b,c三个数补位后继0至长度相同,可能满足情况为

  1. b数字移位 和 c-a的数字移位比较
  2. a数字移位 和 c-b的数字移位比较
  3. b数字移位 和 10*c-a的数字移位比较
  4. a数字移位 和 10*b-a的数字移位比较

模拟一个大数减法,四次比较

#include <bits/stdc++.h>

#define maxn 100005
using namespace std;

char a[maxn],b[maxn],c[maxn];
int numa[maxn],numb[maxn],numc[maxn];
int ans1,ans2,ans3;

void sub(char s1[],char s2[],int n,int m)
{
    int i,j;
    for(i=0; i<n; i++)
        numa[i]=s1[n-i-1]-'0';
    for(i=0; i<m; i++)
        numb[i]=s2[m-i-1]-'0';
    for(i=0; i<n; i++)
        numc[i]=numa[i]-numb[i];
    for(i=0;i<n;i++){
        if(numc[i]<0)
        {
            while(numc[i]<0)
            {
              numc[i+1]=numc[i+1]-1;
              numc[i]+=10;
            }
        }    
    }   
}

bool isbig(char s1[],char s2[],int n,int m)
{
    if(n>m)
        return 1;
    else if(n==m){
        int k=strcmp(s1,s2);
        if(k>0)
            return 1;
    }
    return 0;
}

void input(){
    scanf("%s%s%s",a,b,c);
    int lena=strlen(a);
    int lenb=strlen(b);
    int lenc=strlen(c);
    int len=max(lena,max(lenb,lenc));

    ans1=ans2=ans3=0;

    int tlen=strlen(a);
    while(len>tlen)
    {
        a[tlen]='0';
        a[++tlen]='\0';
        ans1++;
    }

    tlen=strlen(b);
    while(len>tlen)
    {
        b[tlen]='0';
        b[++tlen]='\0';
        ans2++;
    }

    tlen=strlen(c);
    while(len>tlen)
    {
        c[tlen]='0';
        c[++tlen]='\0';
        ans3++;
    }
}

void solve(){
    int lena=strlen(a);
    int lenb=strlen(b);
    int lenc=strlen(c);
    int i;
    if(isbig(c,a,lenc,lena)){
        sub(c,a,lenc,lena);
        int flag=0;
        int Flag=1;
        int cnt=0;
        for(i=lenc-1;i>=0;i--){
            if(!numc[i])
            {
                if(!flag)
                    continue;
                else
                {
                    if(numc[i]!=b[cnt]-'0')
                        Flag=0;
                    cnt++;
                }
            }else{
                flag=1;
                if(numc[i]!=b[cnt]-'0')
                    Flag=0;
                cnt++;
            }
            if(!Flag)break;
        }

        for(i=cnt+1;i<lenb;i++)
            if(b[i]-'0'!=0)
                Flag=0;

        if(Flag){
            ans2-=lenc-cnt;
            if(ans2<0)
            {
                ans1-=ans2;
                ans3-=ans2;
                ans2=0;
            }

            printf("%d %d %d\n",ans1,ans2,ans3);
            return ;
        }
    }

    if(isbig(c,b,lenc,lenb)){
        sub(c,b,lenc,lenb);
        int flag=0;
        int Flag=1;
        int cnt=0;
        for(i=lenc-1;i>=0;i--){
            if(!numc[i])
            {
                if(!flag)
                    continue;
                else
                {
                    if(numc[i]!=a[cnt]-'0')
                        Flag=0;
                    cnt++;
                }
            }else{
                flag=1;
                if(numc[i]!=a[cnt]-'0')
                    Flag=0;
                cnt++;
            }
            if(!Flag)break;
        }

        for(i=cnt+1;i<lena;i++)
            if(a[i]-'0'!=0)
                Flag=0;

        if(Flag){
            ans1-=lenc-cnt;
            if(ans1<0)
            {
                ans2-=ans1;
                ans3-=ans1;
                ans1=0;
            }

            printf("%d %d %d\n",ans1,ans2,ans3);
            return ;
        }
    }

//    cout<<"!";
    c[lenc]='0';
    c[++lenc]='\0';
    ans3++;

    if(isbig(c,a,lenc,lena)){
        sub(c,a,lenc,lena);
        int flag=0;
        int Flag=1;
        int cnt=0;
        for(i=lenc-1;i>=0;i--){
            if(!numc[i])
            {
                if(!flag)
                    continue;
                else
                {
                    if(numc[i]!=b[cnt]-'0')
                        Flag=0;
                    cnt++;
                }
            }else{
                flag=1;
                if(numc[i]!=b[cnt]-'0')
                    Flag=0;
                cnt++;
            }
            if(!Flag)break;
        }

        for(i=cnt+1;i<lenb;i++)
            if(b[i]-'0'!=0)
                Flag=0;

        if(Flag){
            ans2-=lenc-1-cnt;
            if(ans2<0)
            {
                ans1-=ans2;
                ans3-=ans2;
                ans2=0;
            }

            printf("%d %d %d\n",ans1,ans2,ans3);
            return ;
        }
    }

    if(isbig(c,b,lenc,lenb)){
        sub(c,b,lenc,lenb);
        int flag=0;
        int Flag=1;
        int cnt=0;
        for(i=lenc-1;i>=0;i--){
            if(!numc[i])
            {
                if(!flag)
                    continue;
                else
                {
                    if(numc[i]!=a[cnt]-'0')
                        Flag=0;
                    cnt++;
                }
            }else{
                flag=1;
                if(numc[i]!=a[cnt]-'0')
                    Flag=0;
                cnt++;
            }
            if(!Flag)break;
        }

        for(i=cnt+1;i<lena;i++)
            if(a[i]-'0'!=0)
                Flag=0;

        if(Flag){
            ans1-=lenc-1-cnt;
            if(ans1<0)
            {
                ans2-=ans1;
                ans3-=ans1;
                ans1=0;
            }

            printf("%d %d %d\n",ans1,ans2,ans3);
            return ;
        }
    }

    printf("-1\n");
    return;
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        input();
        solve();
    }
}

1006 Final Exam

题目链接
题意:有n门课,课程总分是m,复习一门课程的时间为其分数x+1,要求保证有k门课程通过。求所需要的最少时间。

思路:考虑最糟糕情况,即最后n-k+1门课程平分了所有的m分数。对于前k-1门,我并不知道课程的具体顺序,所以每一门复习所需要的时间都是,对于后面总共n-k+1所需要的时间总和即为m+1。

#include <cstdio>
#include <cmath>
#include <iostream>

typedef long long ll;
using namespace std;

int n,m,k;
void input(){
    scanf("%d%d%d",&n,&m,&k);
}

void solve(){
    printf("%lld\n",(ll)ceil(1.0*(m+1)/(n-k+1))*(k-1)+m+1);
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        input();
        solve();
    }
}

1010 Just Repeat

题目链接
题意:两个人玩游戏,第一人有n张牌,第二个人有m张,每张牌有其花色用数字表示,当其中一人选择了某种花色的牌,另一人就不能选择此花色的牌。谁不能拿谁输,求最后胜者。

思路:贪心考虑,优先的牌一定是出现次数最多的。在我方出现多,代表能选择更多次,在对方出现多代表能封对方更多牌。将双方都出现过的牌进行统计次数,从大到小排序,双方轮流贪心选择。
最后记录每个人的牌数,即只出现在自己方的牌+被自己选择的出现在双方的牌在自己手上出现次数。比较大小,若前者大于后者则胜利,否则失败。

据题解说卡了map,通过排序后类似多项式合并的方式优化。

#include <bits/stdc++.h>

#define maxn 10000005
typedef unsigned long long ull;
using namespace std;

unsigned long long k1, k2;
ull a[maxn],b[maxn];
pair<ull,ull> res[maxn];

bool cmp(pair<ull,ull> p,pair<ull,ull> q){
    return p.first + p.second > q.first + q.second;
}

struct node{
    ull val;
    ull cnt;
};
node na[maxn],nb[maxn];
int n,m,p;
unsigned long long rng() {
    unsigned long long k3 = k1, k4 = k2;
    k1 = k4;
    k3 ^= k3 << 23;
    k2 = k3 ^ k4 ^ (k3 >> 17) ^ (k4 >> 26);
    return k2 + k4;
}

void input(){
    int i;
    scanf("%d%d%d",&n,&m,&p);
    if(p==1){
        for(i=0;i<n;i++)
            scanf("%lld",&a[i]);
        for(i=0;i<m;i++)
            scanf("%lld",&b[i]);
    }else{
        ull mod;
        scanf("%lld%lld%lld",&k1,&k2,&mod);
        for (i = 0; i < n; ++i)
            a[i] = rng() % mod;

        scanf("%lld%lld%lld",&k1,&k2,&mod);
        for (i = 0; i < m; ++i)
            b[i] = rng() % mod;
    }

/*
    for(i=0;i<n;i++)
        cout<<a[i]<<" ";
    cout<<endl;
    for(int j=0;j<m;j++)
        cout<<b[j]<<" ";
    cout<<endl;
*/
}

void solve(){
    int i,j;
    sort(a,a+n);
    ull temp=a[0];
    int cnt=1;
    int cnta=0;
    for(i=1;i<n;i++){
        if(a[i]!=a[i-1]){
            na[cnta].val=temp;
            na[cnta].cnt=cnt;
            cnt=1;
            temp=a[i];
            cnta++;
        }else
            cnt++;
    }
    na[cnta].val=temp;
    na[cnta].cnt=cnt;
    cnta++;

    sort(b,b+m);
    temp=b[0];
    cnt=1;
    int cntb=0;
    for(i=1;i<m;i++){
        if(b[i]!=b[i-1]){
            nb[cntb].val=temp;
            nb[cntb].cnt=cnt;
            cnt=1;
            temp=b[i];
            cntb++;
        }else
            cnt++;
    }
    nb[cntb].val=temp;
    nb[cntb].cnt=cnt;
    cntb++;

    cnt=0;
    ull a=0,b=0;
    for(i=0,j=0;i<cnta&&j<cntb;)
    {
//        cout<<i<<' '<<cnta<<' '<<na[i].val<<' '<<na[i].cnt<<endl;
//        cout<<j<<' '<<cntb<<' '<<nb[j].val<<' '<<nb[j].cnt<<endl;
//        cout<<endl;
        if(na[i].val>nb[j].val){
            b+=nb[j].cnt;
            j++;
        }else if(na[i].val<nb[j].val){
            a+=na[i].cnt;
            i++;
        }else{
            res[cnt++]=make_pair(na[i].cnt,nb[j].cnt);
            i++;j++;
        }
    }

    while(i<cnta){
        a+=na[i].cnt;
        i++;
    }

    while(j<cntb){
        b+=nb[j].cnt;
        j++;
    }

    sort(res,res+cnt,cmp);

    for(i=0;i<cnt;i++)
    {
        if(i%2==0)
            a+=res[i].first;
        else
            b+=res[i].second;
    }
//    cout<<a<<" "<<b<<endl;

    if(a>b) printf("Cuber QQ\n");
    else printf("Quber CC\n");
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        input();
        solve();
    }
}

1011 Kejin Player

题目链接
题意:给出n行输入,第i行表示从第i级升级到第i+1级别需要花费ai费用。对于升级有pi的概率表示可以成功,对于1-pi的概率失败会导致等级返回至xi级。q个询问,求从第a级到第b级需要多少费用期望。

思路:期望dp,设dp[i]为升级到第i级别所需要的费用,其等级为xi=i时

否则,可推出公式

可以通过前缀和维护

#include <bits/stdc++.h>

#define maxn 500005
typedef long long ll;
using namespace std;
const ll mod = 1000000007;

int n, q;
ll predif;
ll dif[maxn];//dp[i+1]-dp[i]
ll presum[maxn];

ll power_mod(ll a, ll b, ll m) {
    ll ans = 1;
    a %= m;
    while (b)
    {
        if (b & 1)ans = (ans*a) % m;
        a = (a*a) % m;
        b >>= 1;
    }
    return ans;
}//power_mod(b,m-2,m)

void input() {
    scanf("%d%d", &n, &q);
    int i;
    ll ri, si, xi, ai;
    presum[0] = 0;
    for (i = 1; i <= n; i++) {
        scanf("%lld%lld%lld%lld", &ri, &si, &xi, &ai);
        ll pi = ri * power_mod(si, mod - 2, mod) % mod;
        ll fpi = power_mod(pi, mod - 2, mod) % mod;
        if (xi == i) {
            dif[i] = -1ll * ai%mod;
            dif[i] = ((dif[i] * fpi )%mod-mod)%mod;
        }
        else {
            dif[i] = (((1 - pi) % mod*(presum[i - 1] - presum[xi-1]) % mod - ai) % mod ) % mod;
            dif[i] = ((dif[i] * fpi )%mod-mod)%mod;
        }
        presum[i] = ((presum[i - 1] + dif[i]) % mod - mod) % mod;
    }
}

void solve() {
    int i;
    int u, v;
    //    for(i=1;i<=n;i++)
    //        cout<<i<<' '<<dif[i]<<endl;
    for (i = 1; i <= q; i++) {
        scanf("%d%d", &u, &v);
        printf("%lld\n", -1ll * (((presum[v - 1] - presum[u - 1]) % mod-mod)%mod));
    }
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        input();
        solve();
    }
}