zoukankan      html  css  js  c++  java
  • 第八届“图灵杯”NEUQ-ACM程序设计竞赛个人赛(同步赛)全题解

    此文转载自:https://blog.csdn.net/StandNotAlone/article/details/113439051

    先在开头吐槽一下这场比赛修改了n次题面甚至改了数据,题面的糟糕程度实属第一次见。
    这场比赛难的不是题目,是出题人,这场比赛可能是没有经过严格的验题。

    A题
    相关tag:数学

    如果我们把1划分成x份,那么每份就是1/x。
    我们希望最后的k个人得到的尽可能平均,那么每个人必然是拿到[x/k]份的1/x或者[x/k]+1份的1/x。
    那么每个人得到的值,与平均值x/k的差值是不可能大于1/x的。

    题目要求差值不超过1/210,那么我们把1切成x=1024份,每份的值为1/1024,再平均分配打包给所有人就是了。

    切割成1024份需要的次数为20+21+22+…+29=1023次,在加上打包k次,次数必定在6000次内。
    (当然你切成2048份也行,仍然在6000次内)

    #include<bits/stdc++.h>
    #define ll long long
    #define INF 0x7f7f7f7f //2139062143
    #define llINF 9223372036854775807
    #define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    using namespace std;
    const int maxn=30+7;
    const int mod=998244353;
    
    int k;
    int ans=1023;
    
    int main()
    {
        scanf("%d",&k);
        printf("%d
    ",ans+k);
        int now=1;
        for(int i=0;i<=9;i++)
        {
            for(int j=0;j<now;j++)
                printf("1 %d
    ",i);
            now*=2;
        }
        int num=1024;
        int a=num/k,b=num%k;
        for(int i=1;i<=k;i++)
        {
            printf("2");
            if(i<=b)
            {
                printf(" %d",a+1);
                for(int i=0;i<=a;i++) printf(" 10");
                printf("
    ");
            }
            else
            {
                printf(" %d",a);
                for(int i=0;i<a;i++) printf(" 10");
                printf("
    ");
            }
        }
    }
    

    B题
    相关tag:前缀和

    使用num[i]记录第i个数字为多少。
    使用sum[i]记录前缀和,也就是前i个数字值的和。

    如果某一个区间[l,r]的数字加起来为k的整数倍。
    那么必定有(num[r]-num[l-1])%k=0。
    也等价于num[r]%k=num[l-1]%k

    我们可以依靠前缀和对k取模的结果。
    cas[i]记录前缀和对k取模为i的前缀和,第一次出现在哪里。
    每次出现取模为i的前缀和时,用当前位置减去第一次出现的位置,即为该位置为右边界可以找到的最长区间了。

    #include<bits/stdc++.h>
    #define ll long long
    #define INF 0x7f7f7f7f //2139062143
    #define llINF 9223372036854775807
    #define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    using namespace std;
    const int maxn=30+7;
    
    ll ans,n,k;
    ll num[100007];
    ll cas[100007];
    
    int main()
    {
        int t;scanf("%d",&t);
        while(t--)
        {
            ans=-1;
            for(int i=1;i<100007;i++) cas[i]=-1;
            scanf("%lld%lld",&n,&k);
            for(int i=1;i<=n;i++)
            {
                scanf("%lld",&num[i]);
                num[i]=(num[i]+num[i-1])%k;
                if(cas[num[i]]==-1) cas[num[i]]=i;
                else ans=max(ans,i-cas[num[i]]);
            }
            printf("%lld
    ",ans);
        }
    }
    

    C题
    相关tag:数学

    首先从左往右看,我们可以按照连续的不下降区间,把整个数组划分成各块区间。
    由于相邻两个区间之间,相邻的那两个数的值必定是下降的。
    因此我们选择满足条件的子数组,只能在每块区间里找。

    对于一个长度为x的区间。
    长度为1的连续子数组有x种取法。
    长度为2的连续子数组有x-1种取法。

    长度为x的连续子数组用1种取法。
    该区间的总取法为(1+2+…+x)=x × imes × (x+1)/2种。

    所有区间的取法加起来即可。

    #include<bits/stdc++.h>
    #define ll long long
    #define INF 0x7f7f7f7f //2139062143
    #define llINF 9223372036854775807
    #define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    using namespace std;
    const int maxn=30+7;
    
    ll ans=0,n;
    ll num[100007];
    ll cas=0;
    
    int main()
    {
        scanf("%lld",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&num[i]);
            if(num[i]>=num[i-1]) cas++;
            else
            {
                ans+=cas*(cas+1)/2;
                cas=1;
            }
        }
        ans+=cas*(cas+1)/2;
        printf("%lld
    ",ans);
    }
    

    D题
    相关tag:简单博弈

    只有1张牌的时候,先手必输。
    有x=[2,k+1]张牌的时候,先手的人可以拿走x-1张牌,剩下1张,所以先手必胜。
    有x=k+2张牌的时候,先手的人不管拿走[1,k]的任意张数,剩下的数量必定落在[2,k+1]的区间里,先手必输。
    当x=[k+3,2k+2]张牌的时候,同上,先手必胜
    当x=2k+3张牌的时候,同上,先手必输。

    归纳后得到,当x=d × imes ×(k+1)+1时候(d为常数),先手必输,其他情况先手必胜。

    #include<bits/stdc++.h>
    #define ll long long
    #define INF 0x7f7f7f7f //2139062143
    #define llINF 9223372036854775807
    #define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    using namespace std;
    const int maxn=1e5+7;
    
    
    int main()
    {
        int t;scanf("%d",&t);
        while(t--)
        {
            int n,k;scanf("%d%d",&n,&k);
            if((n-1)%(k+1)==0) printf("ma la se mi no.1!
    ");
            else printf("yo xi no forever!
    ");
        }
    }
    

    E题
    相关tag:博弈
    (出题人题面的输出和实际样例的输出都能不一样的,一个是no.1!,一个是no1.!。一个题就算了,好几个题的题面都有问题,是真的绝活)

    首先我们算出在乌龟上方和下方分别有a和b张牌。
    如果a>b的话,我们交换一下a,b的值,保证a<=b。

    接下来,按照a的值为0,1,2,…15e5分类讨论。

    a=0的情况:
    a=0,b=0的时候,先手负。
    a=0,b>0的时候,先手可以一次拿光b,先手胜。

    a=1的情况:
    a=1,b=1的时候,先手a和b都拿掉1,先手胜。
    a=1,b=2的时候,先手如果想赢,那么就必须要让当前的状态变为先手负(操作之后当前后手的人变为下次操作的先手),而之前的先手负的状态只有a=0,b=0,我们从a=1,b=2的状态怎么取都是得不到a=0,b=0的。因此此时先手负。
    a=1,b>2的时候,我们可以把b拿掉b-2的部分,使得变成a=1,b=2的先手负(当前的后手)状态。因此先手胜。

    a=2的情况:
    a=2,b>=2的所有状况,都可以从b取走b-1个,使得变为a=2,b=1也就是a=1,b=2的情况。先手必胜。

    a=3的情况:
    a=3,b=3或等于4的时候,可以同时从a和b中拿掉3或者2,得到a=0,b=0或者a=1,b=2。因此先手胜。
    a=3,b=5的时候,前面的先手负状态只有a=0,b=0或者a=1,b=2,我们不论如何操作都无法得到这两种状态。因此先手负。
    a=3,b>5的时候,先手可以从b拿走b-5个,使得剩下a=3,b=5个。因此先手胜

    归纳后实际上会发现,当a>0的情况下,
    对于某一类a=x,先手如果想赢,当b还不是很大的状态下,只能通过同时取走a和b一部分值,得到a<x的某个先手负状态来取胜。当前面没有a和b差值等于当前状态的情况下,那么此时的先手玩家只能把当前状态移动到先手胜的状态,因此当前的先手必败。那么对应的必败状态的a和b的差值,会比前面已经出现过的大1。

    最开始的时候先手负状态只有a=0,b=0,a和b差值为0,
    后续有a=1,b=2,a和b差值为1。
    再后续a=3,b=5,a和b差值为2。
    再后续a=4,b=7,a和b差值为3。

    注意到这里跳过了a=2的状态。这是因为在a<2的状态中有一个a=1,b=2的状态是先手负。
    我们可以把b取b-2个,变为上述状态来获胜。

    也就是说,对于我们当前a=x的状态,如果在前面的a<x的某一个状态中存在b=x使得先手负的,那么a=x的所有情况都是必胜。

    由此综上,用一个dis记录下一个先手负状态a和b的差值应该为多少。
    使用cas[i]记录对于a=i,b=cas[i]时先手负,cas[i]=-1时代表此时b不论取什么值先手必胜。

    那么们可以一路从小到大去循环a的值,
    如果当前的a值,没有在前面计算的b中出现过,代表当前a的值存在一种先手负的状态,当b=a+dis时必败,并且更新cas[b]=-1。
    如果当前的a值已经在前面计算的b中出现过,也就是说cas[a]=-1,那么当前a的值必胜。

    以上。

    #include<bits/stdc++.h>
    #define ll long long
    #define INF 0x7f7f7f7f //2139062143
    #define llINF 9223372036854775807
    #define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    using namespace std;
    const int maxn=3e6+7;
    
    ll cas[maxn];
    ll dis=1;
    
    int main()
    {
        for(int i=1;i<maxn;i++)
        {
            if(cas[i]!=-1)
            {
                cas[i]=i+dis;
                if(i+dis<maxn) cas[i+dis]=-1;
                dis++;
            }
        }
        int t;scanf("%d",&t);
        while(t--)
        {
            ll n,x;scanf("%lld%lld",&n,&x);
            ll a=x-1,b=n-x;
            if(a>b) swap(a,b);
            if(cas[a]==-1||cas[a]!=b) printf("yo xi no forever!
    ");
            else printf("ma la se mi no.1!
    ");
        }
    }
    

    F题
    相关tag:模拟,哈希,卡时间

    数据很大,一开始交了一发试一下,果然tle了。(那你为什么要交)
    加了个快读,用了unordered_map这个O(1)的哈希map就过掉了。

    #include<bits/stdc++.h>
    #define ll long long
    #define INF 0x7f7f7f7f //2139062143
    #define llINF 9223372036854775807
    #define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    using namespace std;
    const int maxn=30+7;
    const int mod=998244353;
    
    int read()      //整数读入挂
    {
        int x=0,f=1;
        char c=getchar();
        while(c<'0'||c>'9')
        {
            if(c=='-') f=-1;
            c=getchar();
        }
        while(c>='0'&&c<='9')
        {
            x=x*10+c-'0';
            c=getchar();
        }
        return x*f;
    }
    
    vector<string>grade[107];
    int n;
    
    struct Node
    {
        int grade,num,sex;
    };
    
    unordered_map<string,Node>MAP;
    char temp[10007];
    
    int main()
    {
        n=read();
        for(int i=0;i<n;i++)
        {
            string name;
            Node a;
            scanf("%s",temp);
            a.grade=read();
            a.sex=read();
            a.num=read();
            int len=strlen(temp);
            for(int i=0;i<len;i++)
             name+=temp[i];
            MAP[name]=a;
            grade[a.grade].push_back(name);
        }
        for(int i=0;i<=100;i++) sort(grade[i].begin(),grade[i].end());
        int k;cin>>k;
        while(k--)
        {
            int ope;cin>>ope;
            if(ope==1)
            {
                string name;
                scanf("%s",temp);
                int len=strlen(temp);
                for(int i=0;i<len;i++)
                 name+=temp[i];
                Node a=MAP[name];
                printf("%d %d %d
    ",a.grade,a.num,a.sex);
            }
            else
            {
                int x;cin>>x;
                for(int i=0;i<grade[x].size();i++)
                {
                    for(int j=0;j<grade[x][i].size();j++)
                        printf("%c",grade[x][i][j]);
                    printf("
    ");
                }
            }
        }
    }
    

    G题
    相关tag:贪心

    看注释吧

    #include<bits/stdc++.h>
    #define ll long long
    #define INF 0x7f7f7f7f //2139062143
    #define llINF 9223372036854775807
    #define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    using namespace std;
    const int maxn=1e5+7;
    const int mod=998244353;
    
    ll num[maxn];
    
    int main()
    {
        int t;scanf("%d",&t);
        while(t--)
        {
            int n;
            ll k;
            scanf("%d%lld",&n,&k);
            ll M=0;
            int tar=0;//记录下派蒙在第几个
            for(int i=1;i<=n;i++)
            {
                scanf("%lld",&num[i]);
                if(num[i]>M)
                {
                    tar=i;
                    M=num[i];
                }
                num[i]+=num[i-1];//前缀和
            }
            ll yici=M+n-1;//代表一次循环最少要吃掉多少个
            if(k<tar-1) printf("NO
    ");//前面的tar个人最少每个人要吃一个
            else
            {
                ll rest=(k-tar+1)%(yici);//代表除了派蒙的人每人都只吃1个的情况,最后剩下的部分
                ll cas=(k-tar+1)/yici;//在上述情况下总共有多少轮循环(这里的循环,派蒙是第一个人)
                cas*=(num[n]-M);//每轮循环我们最多还可以再多吃总的个数减去派蒙的个数
                cas+=num[tar-1]-tar+1;//在开始循环前,一开始就排在派蒙前面的tar-1个人,除了最基本的每人吃一个外,最多还能吃几个
                if(cas>=rest) printf("YES
    ");//如果能多吃的部分大于剩余的部分,代表我们能有一种安排,使得某次循环开始时,派蒙去吃东西的时候k已经为0
                else printf("NO
    ");
            }
        }
    }
    

    H题
    相关tag:简单规律,快速幂

    m只有0,1,2三种状态,稿纸上推演一下,总结规律即可。

    #include<bits/stdc++.h>
    #define ll long long
    #define INF 0x7f7f7f7f //2139062143
    #define llINF 9223372036854775807
    #define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    using namespace std;
    const int maxn=30+7;
    const int mod=998244353;
    
    ll qpow(ll x,ll p)
    {
        ll ret=1;
        while(p)
        {
            if(p&1) ret=ret*x%mod;
            x=x*x%mod;
            p>>=1;
        }
        return ret;
    }
    
    int main()
    {
        int t;scanf("%d",&t);
        while(t--)
        {
            ll n,m;scanf("%lld%lld",&n,&m);
            if(m==2) printf("%lld
    ",qpow(2,n));
            else if(m==1)
            {
                if(n==0) printf("1
    ");
                else printf("%lld
    ",n*2);
            }
            else
            {
                if(n<2) printf("%lld
    ",n+1);
                else printf("%lld
    ",n+2);
            }
        }
    }
    

    I题
    相关tag:数学,暴力

    如果我们选择了k天,第一天选择了d朵。
    那么总的花的数量就是d × imes × (20+21+…+2k-1
    也就是说对于选择第k天的情况来说,如果存在满足的整数d,那么总的花数量n需要满足n能整除 (20+21+…+2k-1
    因此我们直接处理出k=2到15这些天数的 (20+21+…+2k-1),用n一一去暴力尝试整除即可。

    #include<bits/stdc++.h>
    #define ll long long
    #define INF 0x7f7f7f7f //2139062143
    #define llINF 9223372036854775807
    #define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    using namespace std;
    const int maxn=30+7;
    
    ll cas[20];
    
    int main()
    {
        cas[1]=1;
        for(int i=2;i<=15;i++) cas[i]=cas[i-1]*2;
        for(int i=2;i<=15;i++) cas[i]+=cas[i-1];
        int t;scanf("%d",&t);
        while(t--)
        {
            ll n;scanf("%lld",&n);
            bool flag=0;
            for(int i=2;i<=15;i++) if(n%cas[i]==0) flag=1;
            if(flag) printf("YE5
    ");
            else printf("N0
    ");
        }
    }
    

    J题
    相关tag:模拟

    模拟,没什么好说的。大一同学去学一下如何存图。

    #include<bits/stdc++.h>
    #define ll long long
    #define INF 0x7f7f7f7f //2139062143
    #define llINF 9223372036854775807
    #define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    using namespace std;
    const int maxn=30+7;
    const int mod=998244353;
    
    int n,m;
    int field[307][307];
    bool flag[307];
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;i++)
        {
            int a,b,w;scanf("%d%d%d",&a,&b,&w);
            if(field[a][b]==0) field[a][b]=field[b][a]=w;
            else field[a][b]=field[b][a]=min(field[a][b],w);
        }
        ll ans=llINF;
        int k;scanf("%d",&k);
        while(k--)
        {
            for(int i=1;i<=n;i++) flag[i]=0;
            int cnt=0;
            ll sum=0;
            bool f=1;
            int x;scanf("%d",&x);
            int now=0;
            while(x--)
            {
                int to;scanf("%d",&to);
                if(field[now][to]==0) f=0;
                sum+=field[now][to];
                now=to;
                if(flag[to]==0) {flag[to]=1;cnt++;}
            }
            if(field[now][0]==0||cnt!=n) f=0;
            sum+=field[now][0];
            if(f) ans=min(ans,sum);
    
        }
        if(ans==llINF) printf("-1
    ");
        else printf("%lld
    ",ans);
    }
    

    K题
    相关tag:模拟

    注意一下字符为z或者Z的特殊情况即可。
    另外出题人爬

    #include<bits/stdc++.h>
    #define ll long long
    #define INF 0x7f7f7f7f //2139062143
    #define llINF 9223372036854775807
    #define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    using namespace std;
    
    vector<char>c[4];
    vector<int>num[4];
    int cntc=0,cntn=0;
    
    char change(char c)
    {
        if(c=='Z') return 'b';
        if(c=='z') return 'B';
        return c+1;
    }
    
    int main()
    {
        string s;cin>>s;
        for(int i=0;i<32;i++)
        {
            if(s[i]>='0'&&s[i]<='9')
            {
                num[cntn].push_back(s[i]-'0');
                cntn=(cntn+1)%4;
            }
            else
            {
                c[cntc].push_back(s[i]);
                cntc=(cntc+1)%4;
            }
        }
        for(int i=0;i<4;i++)
        {
            for(int j=0;j<4;j++)
            {
                for(int k=0;k<num[i][j];k++)
                    c[i][j]=change(c[i][j]);
            }
        }
        for(int i=0;i<4;i++)
        {
            for(int j=3;j>=0;j--)
                printf("%c",c[j][i]);
        }
        printf("
    ");
    }
    

    L题
    相关tag:二分答案

    很好的一个二分答案例题。
    对于我们最后站台之间相邻距离的最大值L,随着L的增大,我们需要设置的站台数量只可能变少不可能变多。满足二分条件。存在某一个值x,使得当L>=x时,站台数量<=k。
    可以借此写出一个二分。

    对于每对相邻的车站,他们的距离如果为dis,我们check的距离为x。
    那么这段距离之中除了左右两侧已经有的城市外,需要的站台数量就是(dis/x+dis%x?1:0)-1。
    由此算出距离x对应需要的最少站台数量sum,用sum与题目要求k对比即可check正确性。

    #include<bits/stdc++.h>
    #define ll long long
    #define INF 0x7f7f7f7f //2139062143
    #define llINF 9223372036854775807
    #define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    using namespace std;
    const int maxn=1e5+7;
    
    ll num[maxn];
    ll dis[maxn];
    int n,k;
    
    bool check(ll x)
    {
        ll sum=0;
        for(int i=1;i<n;i++)
        {
            if(dis[i])
            {
                ll temp=dis[i]/x;
                if(dis[i]%x) temp++;
                sum+=temp-1;
            }
        }
        return sum<=k;
    }
    
    int main()
    {
        scanf("%d%d",&n,&k);
        for(int i=0;i<n;i++) scanf("%lld",&num[i]);
        sort(num,num+n);
        bool flag=1;
        for(int i=1;i<n;i++)
        {
            dis[i]=num[i]-num[i-1];
            if(dis[i]!=0) flag=0;
        }
        if(flag) printf("0
    ");
        else
        {
            ll l=1,r=1e12;
            while(l<r)
            {
                ll mid=(l+r)>>1;
                if(check(mid)) r=mid;
                else l=mid+1;
            }
            printf("%lld
    ",l);
        }
    }
    
       

    更多内容详见微信公众号:Python测试和开发

    Python测试和开发

  • 相关阅读:
    韩式英语
    Daily dictation 听课笔记
    words with same pronunciation
    you will need to restart eclipse for the changes to take effect. would you like to restart now?
    glottal stop(britain fountain mountain)
    education 的发音
    第一次用Matlab 的lamada语句
    SVN的switch命令
    String的split
    SVN模型仓库中的资源从一个地方移动到另一个地方的办法(很久才解决)
  • 原文地址:https://www.cnblogs.com/phyger/p/14357327.html
Copyright © 2011-2022 走看看