• 华东交通大学2020年ACM“双基”程序设计竞赛 题解


    A 签到

    题解:直接输出即可,注意行末换行。

    #include <iostream>
    #include <fstream>
    using namespace std;
    int main(){
        
        int n,m;
        freopen("in.txt","r",stdin); //输入重定向,输入数据将从in.txt文件中读取 
        freopen("out.txt","w",stdout); //输出重定向,输出数据将保存out.txt文件中 
    
        cout<<"祝贺祖国成立71周年!"<<endl;
        
        return 0;
    }
    View Code

    B 不要666升级版

    题解:

    本题可以用数位DP来解:

    首先定义状态dp[len][sum1][sum2] 表示长度为len对6取模为sum1,各位上的数字和为sum2。

    然后把一个数拆成a + b的形式,如878327 为 800000 + 78327。

    由(a + b)^3 = a ^3 + b ^ 3 + 3 * a^2 * b + 3 * a * b ^ 2。

    那么所有满足条件的数的立方和,其中cnt为这些数字的个数:

    a ^ 3 * cnt + (b ^ 3 + c ^ 3 + …) + 3 * a ^ 2 * (b + c + …) + 3 * a * (b ^ 2 + c ^ 2 +….)

    我们发现:(b^3 + c ^ 3 + ….)为立方和,(b + c + …. )为和,(b ^2 + c ^ 2 + …)为平方和,所以我们需要维护4个值:这些数字的个数,和,平方和,立方和就可以解决这题了。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int b[25];
    ll bit[25];
    const ll mod = 1e9 + 7;
    struct st{
        ll num;
        ll sum;
        ll sqsum;
        ll cusum; 
        st(ll _num,ll _sum,ll _sqsum,ll _cusum):num(_num),sum(_sum),sqsum(_sqsum),cusum(_cusum){
        }
        st():num(-1),sum(0),sqsum(0),cusum(0){
        }
    }dp[25][10][180];
    st dfs(int pos, int num1, int num2, bool state){
        if(pos ==- 1){
            if(num1 == 0 || num2 % 6 == 0){
                return st(0,0,0,0);
            }
            return st(1, 0, 0, 0);
        }
        if(!state && dp[pos][num1][num2].num != -1){
            return dp[pos][num1][num2];
        }
        int up = state ? b[pos] : 9;
        st ans;
        ans.num = 0;
        for(int i = 0;i <= up; i++){
            if(i == 6){
                continue;
            }
            st tem = dfs(pos - 1, (num1 * 10 + i) % 6, num2 + i, state && i == up);
            ans.num = (ans.num + tem.num) % mod;
            ans.sum = ( ans.sum + i * bit[pos] % mod * tem.num % mod + tem.sum) % mod;
               ans.sqsum = (ans.sqsum + tem.sqsum) % mod;
               ll temp = i * bit[pos] % mod;
               ans.sqsum = (ans.sqsum + temp * temp % mod * tem.num % mod) % mod;
               ans.sqsum = (ans.sqsum + 1ll * 2 * temp % mod * tem.sum % mod) % mod;
               ans.cusum = (ans.cusum + tem.cusum) % mod;
               ans.cusum = (ans.cusum + temp * temp % mod * temp % mod * tem.num % mod) % mod;
               ans.cusum = (ans.cusum + 1ll * 3 * temp % mod * temp % mod * tem.sum % mod) % mod;
               ans.cusum = (ans.cusum + 1ll * 3 * temp * tem.sqsum % mod) % mod;
        }
        if(!state){
            dp[pos][num1][num2] = ans;
        }
        return ans;
    }
    ll solve(ll m){
        int len = 0;
        while(m){
            b[len++] = m % 10;
            m /= 10;
        }  
        return dfs(len-1, 0, 0, true).cusum;
    }
    int main(){
        bit[0] = 1;
        for(int i = 1; i <= 20; i++){
            bit[i] = bit[i-1] * 10%mod;
        }
        ll n, m;
    //    srand((unsigned int)time(NULL));
    //    freopen("test3.out","w",stdout);
    //    freopen("test3.in","r",stdin);
        
    //    int t = 5000;
    //    while(t--){
    //        n = ((double)rand() / RAND_MAX) * 1000000000000000000;
    //        m = ((double)rand() / RAND_MAX) * 1000000000000000000;
    //        if(n > m){
    //            n = n ^ m;
    //            m = n ^ m;
    //            n = n ^ m;
    //        }
    //        if(n < 0 || m < 0) continue;
    //        printf("%lld %lld
    ", n, m);
    //    }
    //    
        while(scanf("%lld%lld",&n, &m)!=EOF){
            printf("%lld
    ",(solve(m) - solve(n - 1) + mod) % mod);
        }
        return 0;
    }
    View Code

    C 欧涛的生日聚会

    题解:

    如果整个图没有环的话,显然最多能分的种类数是每个连通分量内最长链的长度之和。

    如果整个图是由若干个不相交的环构成的话,最多能分的种类数是所有环长度的最大公约数(找环的时候,可以从连通块内的任意一点开始编号,第二次经过一个点的时候,它第二次的编号减去第一次的编号就是环的大小)。

    除了这两种特殊情况之外,还有一种情况是两个环之间可能有公共部分。

    我们 DFS 的时候可以倒推:建图的时候不仅连一条uv,边权为 1 的边之外,同时连一条 vu,边权为 −1 的边(这种连边方式可以确保我们从任意一个点开始,都可以遍历整个连通块)。

    对于每个连通块,我们还是任意选一个点开始编号,经过一条边的时候将编号加上边权,和上面一样,第二次经过同一个点的时候,它第二次的编号减去第一次的编号就是环的大小。

    这样我们就可以找出所有的环了。

    (最后别忘了题目要求面具最少要有三种)

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <set>
    #define INF 1e9
    using namespace std;
    struct edge
    {
     int v,w;
     bool operator<(const edge&a)const
     {
      return v<a.v||(v==a.v&&w<a.w);
     }
    };
    set<edge> e[100005];
    int dis[100005],vis[100005],ans,res,cnt,maxv,minv;
    int gcd(int x,int y)
    {
     return y==0?x:gcd(y,x%y);
    }
    void dfs(int u,int d)
    {
     if(dis[u])
     {
      ans=gcd(ans,abs(d-dis[u]));
      return;
     }
     dis[u]=d,vis[u]=1;
     maxv=max(maxv,dis[u]);
     minv=min(minv,dis[u]);
     for(auto i:e[u])
      dfs(i.v,d+i.w);
    }
    int main()
    {
     int n,m;
     scanf("%d%d",&n,&m);
     for(int i=1;i<=m;i++)
     {
      int u,v;
      scanf("%d%d",&u,&v);
      e[u].insert({v,1});
      e[v].insert({u,-1});
     }
     for(int i=1;i<=n;i++)
      if(!vis[i])
      {
       maxv=-INF,minv=INF;
       dfs(i,1);
       res+=maxv-minv+1;
      }
     if(ans)
     {
      if(ans<3)puts("-1 -1");
      else
       for(int i=3;i<=ans;i++)
        if(ans%i==0)
        {
         printf("%d %d
    ",ans,i);
         break;
        }
     }
     else
     {
      if(res<3)puts("-1 -1");
      else printf("%d 3
    ",res);
     }
     return 0;
    }
    View Code

    D 欧涛玩游戏

    题解:

    直接判断输入的时候可以构成直角三角形即可,注意多组输入、不要用int 否则是答案错误

    #include <iostream>
    #include <fstream>
    using namespace std;
    int main(){
        int a,b,c;
    //    ifstream infile;   //输入流
    //    ofstream outfile;   //输出流
    // 
    //    infile.open("C:\Users\z1164\Desktop\校赛数据\玩游戏.in", ios::in);
        while (cin>>a>>b>>c)            // 若未到文件结束一直循环
        {
    //        infile >> a >> b >> c;
            //cout<<a<<" "<<b<<" "<<c<<endl;
            if ((a*a==b*b+c*c)||(b*b==a*a+c*c)||(c*c==a*a+b*b)) 
            cout<<"YES"<<endl;
            else cout<<"NO"<<endl;
        }
        //cin>>a>>b>>c;
        
        return 0;
    }
    View Code

    E 生而为人、我很抱歉

    题解:

    题目描述可能有问题、只需要考虑输入的字符串彼此之间是否符合字典序,不用考虑单个字符串是否是按字典序排序。

    #include<bits/stdc++.h>
    using namespace std;
    
    int main(){
        int n;
        int m;
        
    
    
        while(cin>>n>>m){
                string s1[101];
            string s2[105];
            int flag=1;
            
        for(int i=1;i<=n;i++){
            cin>>s1[i];
            s2[i]=s1[i];
        }
        sort(s2+1,s2+1+n);
        for(int i=1;i<=n;i++){
            if(s2[i]!=s1[i]){
                flag=0;
                break;
            }
        }
        if(flag)puts("YES");
        else puts("NO");
        }
    }
    View Code

    F 糖果店

    题解:

    可以看出 Bob可以购买的是 这个数组包含最大值和最小值的子串个个数,因此我们从左到右遍历这个序列,用两个指针t1,t2 来表示到目前为止,最大值和最小值位置的最大值,则当前位置对答案的贡献就是

    min( t1 + t2 )。

    Jerry就容易很多,统计出最大值和最小值的数量,则最终的答案就是从最大值构成的集合中选取非0个个数的方式*从最小值构成的集合中选取非0个个数的方式*从除去最大值和最小值构成的集合中选取任意个数的方式。

    ps:c(n,0) + c(n,1) + c(n,2) + .....+ c(n,n) = 2n.

    题意中忘了写模数,表示非常抱歉

    #include<iostream>
    #include<cstring>
    #include<algorithm> 
    using namespace std;
    #define ll long long
    const ll inf=0xffffff;
    const ll mod=1000000007;
    ll n,m,k,t;
    ll num[100010];
    ll cal(ll a,ll b)
    {
        ll ans=1;
        while(b)
        {
            if(b&1)
            ans=ans*a%mod;
            a=a*a%mod;
            b>>=1;
        }
        return ans;
    }
    int main()
    {
        cin>>t;
        while(t--)
        {
            cin>>n;
            ll min1=inf,max1=0,min_num=0,max_num=0;
            ll sum1=0,sum2=0;
            for(int i=1;i<=n;i++)
            {
                cin>>num[i];
                min1=min(min1,num[i]);
                max1=max(max1,num[i]);
            }
            if(min1==max1)
            {
                sum1=n*(n+1)/2%mod;
                sum2=cal(2,n)-1;
                cout<<sum1<<' '<<sum2<<endl;
                continue;
            }
            ll t1=0,t2=0;
            for(int i=1;i<=n;i++)
            {
                if(min1==num[i])
                {
                    t1=i;
                    min_num++;
                }
                if(max1==num[i])
                {
                    t2=i;
                    max_num++;
                }
                sum1=(min(t1,t2)+sum1)%mod;
            } 
            sum2=((cal(2,min_num)-1)%mod*(cal(2,max_num)-1)%mod*cal(2,n-max_num-min_num))%mod;//排列组合 
            /*sum2=(cal(2,n)-cal(2,n-max_num)-cal(2,n-min_num)+cal(2,n-max_num-min_num))%mod;//容斥原理 
            if(sum2<0)
            sum2+=mod;*/
            cout<<sum1<<' '<<sum2<<endl; 
        }
        return 0;
    }
    View Code

    G 钥匙

    题解:

    设lock[i]表示:有 i个槽的钥匙的个数。
    设one[i]表示:有 i个槽且左边第一个槽深度为1 的钥匙的个数。
    设two[i]表示:有 i个槽且左边第一个槽深度为2 的钥匙的个数。
    ..
    ..
    设six[i]表示:有 i个槽且左边第一个槽深度为6的钥匙的个数。


    则显然:lock[i]=one[i]+two[i]+ three[i]+four[i]+five[i]+six[i]
    由于易知:one[i]=six[i],two[i]=three[i]=four[i]=five[i]
    则可以得到:lock[i]= one[i]*2+two[i]*4
    其中:
    one[i]=one[i-1]+two[i-1]+three[i-1]+four[i-1]+five[i-1]+a[i];
    =one[i-1]+two[i-1]*4 +a[i]
    two[i]=one[i-1]*2+two[i-1]*4 + b[i]
    one[i]=one[i-1]+two[i-1]+three[i-1]+four[i-1]+five[i-1]+a[i]=one[i-1]+two[i-1]*4 +a[i]
    two[i]=one[i-1]*2+two[i-1]*4 +b[i]


    其中,a[i] 和b[i]的含义分别是:
    a[i]表示“有 i个槽且左边第一个槽深度为1,同时这种钥匙在除掉第一个槽后不再是钥匙”的钥匙的个数。
    例如 123,124,125,134,135,145,126,136,146,156,1233
    b[i]表示“有 i个槽且左边第一个槽深度为2,同时这种钥匙在除掉第一个槽后不再是钥匙” 的钥匙的个数。
    关键的是求a[i]和b[i],我们可以得到如下表达式:
    a[i]=(2^(i-1)-2)*6+(2^(i-2)-1)*4
    b[i]=(2^(i-1)-2)*9


    a[i] 的各部分的含义如下:
    (2^(i-1)-2)*6:
    当去掉第一位,后面i-1位只有 (2,3)或者 (2,4) 或者(2,5) 或者(3,4) 或者(3,5) 或者(4,5)两种数字的时候,显然是不合法的钥匙(不满足至少有3个不同的深度),加上第一位的1则显然是一个合格的钥匙。所以后面的数字可以为一个组合中两个数字的任意一个,根据乘法原理i-1位一共有2^(i-1)种组合,当然还需要去掉两种特殊情况,就是后面i-1位完全相同的情况。满足这种条件的一共有上面六种组合,所以得到(2^(i-1)-2)*6。
    (2^(i-2)-1)*4:
    当去掉第一位,后面i-1位只有 (2,6)或者 (3,6) 或者(4,6) 或者(5,6)两种数字的时候,显然是不合法的钥匙(不满足至少有3个不同的深度),加上第一位的1则“可能”是一个合格的钥匙。因为要注意满足“相连的槽其深度之差不得为5”这个条件,所以,紧跟着1的绝对不能是6,这样,相对于上面的分析,后面i-2位可以是每组中的任意一个,所以有2^(i-2),还要减去1是因为同样要排除后面全部是和第2位一样的数字这样的情况。满足这种条件的一共有上面的四种组合,所以得到(2^(i-2)-1)*4。

    b[i] 的含义如下:
    (2^(i-1)-2)*9:
    当去掉第一位,后面i-1位只有 (1,3)或者 (1,4) 或者(1,5) 或者(3,4) 或者(3,5) 或者(3,6) 或者(4,5) 或者(4,6) 或者(5,6) 两种数字的时候,显然是不合法的钥匙(不满足至少有3个不同的深度),加上第一位的1则显然是一个合格的钥匙。所以后面的数字可以为一个组合中两个数字的任意一个,根据乘法原理i-1位一共有2^(i-1)种组合,当然还需要去掉两种特殊情况,就是后面i-1位完全相同的情况。满足这种条件的一共有上面9种组合,所以得到(2^(i-1)-2)*9。

    综上我们就可以推出
    lock[i]=one[i]*2+two[i]*4
    one[i]=six[i]
    two[i]=three[i]=four[i]=five[i]
    one[i] = one[i-1]+two[i-1]*4 +a[i]
    two[i] = one[i-1]*2+two[i-1]*4 +b[i]
    a[i]=(2^(i-1)-2)*6+(2^(i-2)-1)*4
    b[i]=(2^(i-1)-2)*9

    #include<cstdio>
    #include<algorithm>
    #define ll long long
    using namespace std;
    ll s[3][30];
    ll ans[30];
    ll pow_1(int a){
        int r=1;
        for(int i=1;i<=a;i++)
        r*=2;
        return r;
    }
    int main(){
        int n;
        s[1][2]=0;
        s[2][2]=0;
        ans[1]=0;
        ans[2]=0;
        for(int i=3;i<=25;i++){
            s[1][i]=s[1][i-1]+4*s[2][i-1]+6*(pow_1(i-1)-2)+4*(pow_1(i-2)-1);
            s[2][i]=2*s[1][i-1]+4*s[2][i-1]+9*(pow_1(i-1)-2);
            ans[i]=2*s[1][i]+4*s[2][i];
        }
        while(scanf("%d",&n)!=EOF){
            printf("%lld
    ",ans[n]);    
        }
        return 0;
        
    }
    View Code
  • 相关阅读:
    将某个MySQL库中的UTF8字符列都转成GBK格式
    挺苹果的声音,iPhone 5s的两处进步
    Cookie 路径在本机测试及服务器部署,在浏览器处理方式上的不同
    inner join、left join、right join中where和and的作用
    TIA Portal V12不能添加新的CPU
    Linux下可执行程序调试信息的分离及release程序的调试
    STM32.定时器
    STM32.SPI(25Q16)
    模电之运放篇
    Zigbee学习
  • 原文地址:https://www.cnblogs.com/tombraider-shadow/p/13969740.html
Copyright © 2020-2023  润新知