• 大连海事大学第十届程序设计竞赛 题解


    题目链接:传送门

    题目难度排序:
    I 、J(题面比较坑) 、A(会算相交就很简单了) < C 、G 、D< K、E < B、H < F
    (个人认为的难度排序)

    前情提要:read可以近似等价于scanf,前面一堆宏定义可以忽略不看。

    A.宣传比赛
    看似计算几何,实则简单暴力,对于每一个海报,向后遍历算出相交面积并求和。
    坑点:注意只有一张海报的情况

    AC

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cctype>
    using namespace std;
    typedef long long LL;
    typedef pair<int,int> pii;
    typedef pair<LL,LL> pLL;
    const int N=5e3;
    const double inf=0x3f3f3f3f;
    #define ls (i<<1)
    #define rs (i<<1|1)
    #define fi first
    #define se second
    #define mk make_pair
    #define pb push_back
    #define mem(a,b) memset(a,b,sizeof(a))
    #define debug puts("...")
    LL read()
    {
        LL x=0,t=1;
        char ch=getchar();
        while(!isdigit(ch)){ if(ch=='-')t=-1; ch=getchar(); }
        while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); }
        return x*t;
    }
    LL x1[N],yy1[N],x2[N],y2[N];
    int main()
    {
        int n=read();
        for(int i=1;i<=n;i++)
        {
            x1[i]=read();
            yy1[i]=read();
            x2[i]=read();
            y2[i]=read();
        }
        LL ans=1,maxn=0;
        for(int i=1;i<=n;i++)
        {
            LL sum=0;
            for(int j=i+1;j<=n;j++)
            {
                LL l=min(x2[i],x2[j])-max(x1[i],x1[j]);
                LL r=min(y2[i],y2[j])-max(yy1[i],yy1[j]);
                if(l<0||r<0) continue;
                sum+=l*r;
            }
            if(sum>maxn)
            {
                maxn=sum;
                ans=i;
            }
        }
        printf("%lld %lld
    ",ans,maxn);
        return 0;
    }
    

    B.DPJ同学来到了魔法王国
    【由codeforces题目改编】
    解析及代码见 :传送门

    C.魔法游戏
    如果由一个数字的xi<n/m ,那么我们肯定要操作使其他数字增加,把他们补充到 xi,使得xi的数量变大,那么代码就出来了,直接暴力的做法是O(n^2),需要遍历xj(j!=i) 来补充自身,但是我们是要使答案最小,所以能找相近的xj就找相近的。但显然会超时,所以在此给定两种做法:

    第一种:
    O(nlogm)
    把思路转化,之前思路是遍历xj让不满足n/m的xi得到补充,而反过来就是把xj多余的数量加到不够n/m的xi上。

    将输入的数字a[k] 增加(如果 xk = a[k] % m 的数量已到达 n/m),即向上查找一个最小的xi,并且xi的数量是不够的。

    那么维护一个集合set。先把所有xi(0~m-1)插入 一个集合set。如果有满足题意的xi (xi==n/m),就把它从集合删掉,否则就在集合中查找比他大的第一个数(二分),再把差值加到答案上。

    AC1

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int,int> pii;
    typedef pair<LL,LL> pLL;
    const int N=2e5+5;
    const double inf=0x3f3f3f3f;
    #define ls (i<<1)
    #define rs (i<<1|1)
    #define fi first
    #define se second
    #define mk make_pair
    #define pb push_back
    #define mem(a,b) memset(a,b,sizeof(a))
    #define debug puts("...")
    LL read()
    {
        LL x=0,t=1;
        char ch=getchar();
        while(!isdigit(ch)){ if(ch=='-')t=-1; ch=getchar(); }
        while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); }
        return x*t;
    }
    int cnt[N];
    int main()
    {
        int n=read(),m=read();
        for(int i=1;i<=n;i++)
        {
            int x=read();
            cnt[x%m]++;
        }
        LL ans=0;
        int k=n/m;
        for(int i=0;i<m;i++)
        {
            if(cnt[i]<=k) continue;
            ans+=cnt[i]-k;
            cnt[i+1]+=cnt[i]-k;
            cnt[i]=k;
    
        }
        cnt[0]+=cnt[m];
        for(int i=0;i<m;i++)
        {
            if(cnt[i]<=k) continue;
            ans+=cnt[i]-k;
            cnt[i+1]+=cnt[i]-k;
        }
        printf("%lld
    ",ans);
        return 0;
    }
    

    做法二:
    O(n+m)
    因为要找最近的,那可以直接一边循环把多余的数字往后一个数字加,就相当于是整体的多余的数字,一起找一个最近的数字了。
    一遍循环后,前面的没有达到n/m的xi仍然不够,但靠后的已经得到补充了,并且所有多余的都会汇集到 x[m] , 而m%m=0,所以把x[m]的个数加到x[0]上,再做一遍遍历即可。

    AC2

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int,int> pii;
    typedef pair<LL,LL> pLL;
    const int N=2e5+5;
    const double inf=0x3f3f3f3f;
    #define ls (i<<1)
    #define rs (i<<1|1)
    #define fi first
    #define se second
    #define mk make_pair
    #define pb push_back
    #define mem(a,b) memset(a,b,sizeof(a))
    #define debug puts("...")
    LL read()
    {
        LL x=0,t=1;
        char ch=getchar();
        while(!isdigit(ch)){ if(ch=='-')t=-1; ch=getchar(); }
        while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); }
        return x*t;
    }
    int cnt[N];
    int main()
    {
        int n=read(),m=read();
        for(int i=1;i<=n;i++)
        {
            int x=read();
            cnt[x%m]++;
        }
        LL ans=0;
        int k=n/m;
        for(int i=0;i<m;i++)
        {
            if(cnt[i]<=k) continue;
            ans+=cnt[i]-k;
            cnt[i+1]+=cnt[i]-k;
            cnt[i]=k;
    
        }
        cnt[0]+=cnt[m];
        for(int i=0;i<m;i++)
        {
            if(cnt[i]<=k) continue;
            ans+=cnt[i]-k;
            cnt[i+1]+=cnt[i]-k;
        }
        printf("%lld
    ",ans);
        return 0;
    }
    

    D. 今天你吃药了吗?
    卡特兰数模板题(会就是会,不会就是不会……),
    也可以用计数DP(动态规划简称)。

    卡特兰数AC代码

    #include <bits/stdc++.h>
    using namespace std;
    int h[110][105];
    void init()
    {
        h[0][0]=1;
        h[0][1]=1;
        h[1][0]=1;
        h[1][1]=1;
        for(int i=2;i<=100;i++)
        {
            for(int j=1;j<=h[i-1][0];j++)
                h[i][j]=h[i-1][j]*(4*i-2);
            for(int j=1;j<=100;j++)
            {
                h[i][j+1]+=h[i][j]/10;
                h[i][j]=h[i][j]%10;
            }
            h[i][0]=100;
            while(h[i][h[i][0]]==0)
                h[i][0]--;
            int t=0;
            for(int j=h[i][0];j>=1;j--)
            {
                t=t*10+h[i][j];
                h[i][j]=t/(i+1);
                t%=(i+1);
            }
            while(h[i][h[i][0]]==0)
                h[i][0]--;
        }
    }
    int main()
    {
        //freopen("input.txt","r",stdin);
        //freopen("output.txt","w",stdout);
        init();
        int n;
        while(scanf("%d",&n),n)
        {
            for(int i=h[n][0];i>=1;i--)
                printf("%d",h[n][i]);
            puts("");
        }
        return 0;
    }
    

    DP:
    状态转移:dp[i][j]=dp[i-1][j]+dp[i][j-1];(i表示有i片完整,j表示有j片不完整)
    最终答案为:dp[n][n];
    AC代码

    #include <bits/stdc++.h>
    using namespace std;
    struct node
    {
        int num[105];
        node operator + (const node b)const
        {
            node res;
            memset(res.num,0,sizeof(res.num));
            for(int i=1;i<=max(num[0],b.num[0]);i++)
                res.num[i]=num[i]+b.num[i];
            res.num[0]=max(num[0],b.num[0]);
            for(int i=1;i<=res.num[0];i++)
            {
                res.num[i+1]+=(res.num[i]/10);
                res.num[i]=res.num[i]%10;
            }
            res.num[0]++;
            while(res.num[res.num[0]]==0)
                res.num[0]--;
            return res;
        }
    }dp[110][110];
    void init()
    {
        for(int i=0;i<=105;i++)
        {
            dp[i][0].num[1]=1;
            dp[i][0].num[0]=1;
        }
        for(int i=1;i<=100;i++)
            for(int j=1;j<=i;j++)
                dp[i][j]=dp[i][j-1]+dp[i-1][j];
    }
    int main()
    {
        int n;
        init();
        while(scanf("%d",&n),n)
        {
            for(int i=dp[n][n].num[0];i>=1;i--)
                printf("%d",dp[n][n].num[i]);
            printf("
    ");
        }
        return 0;
    }

    E. lcy和最小生成树
    思路:求补图的连通块个数,bfs,不过由于是完全图,边数过多,所以每找到一个点要删去一个点。这里面涉及一些贪心思想,首先要是生成树权值最小,肯定是尽量使生成树中更多的边的边权为0,因此将0视为连边,1视为断开,求连通块个数再-1即时答案。

    #include<bits/stdc++.h>
    using namespace std;
    #define maxn 200005
    #define INF 0x3f3f3f3f
    #define inc(i,x,y) for(int i=x;i<=y;i++)
    #define ll long long int
    int n,m;
    set<int>g[maxn],vis;
    bool vv[maxn];
    void dfs(int x){
        vv[x]=1;
        vis.erase(x);
        set<int>::iterator it;
        vector<int>v;
        for(it=vis.begin();it!=vis.end();it++){
            int xx=*it;
            if(g[x].count(xx)==0){
                v.push_back(xx);
            }
        }
        for(int i=0;i<v.size();i++){
            vis.erase(v[i]);
        }
        for(int i=0;i<v.size();i++){
            dfs(v[i]);
        }
    }
    int main()
    {
        //freopen("input8.txt","r",stdin);
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            for(int i=0;i<=n;i++){vv[i]=0;g[i].clear();}
            vis.clear();
            int x,y;
        for(int i=1;i<=m;i++){
            scanf("%d%d",&x,&y);
            g[x].insert(y);
            g[y].insert(x);
        }
        inc(i,1,n)vis.insert(i);
        int ans=0;
        inc(i,1,n){
            if(vv[i]==0){
                ans++;
                dfs(i);
            }
        }
        printf("%d
    ",ans-1);
        }
    
        return 0;
    }
    

    F. Lcy疯狂补作业
    在这里插入图片描述
    在这里插入图片描述
    AC代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=1e6+5;
    int n,m;
    ll p,ans;
    ll c[N],inv[N];
    int prime[N];
    bool vis[N];
    int mu[N],phi[N];
    void mobius_euler()
    {
        memset(vis,0,sizeof(vis));
        mu[1]=1;
        phi[1]=1;
        int cnt=0;
        for(int i=2;i<N;i++)
        {
            if(!vis[i])
            {
                prime[++cnt]=i;
                mu[i]=-1;
                phi[i]=i-1;
            }
            for(int j=1;j<=cnt&&i*prime[j]<N;j++)
            {
                int tmp=i*prime[j];
                vis[tmp]=1;
                if(i%prime[j]==0)
                {
                    mu[tmp]=0;
                    phi[tmp]=phi[i]*prime[j];
                    break;
                }
                else
                {
                    mu[tmp]=-mu[i];
                    phi[tmp]=phi[i]*phi[prime[j]];
                }
            }
        }
    }
    void init()
    {
        inv[1]=1;
        for(int i=2;i<=min(n,m);i++)//递推求逆元
            inv[i]=inv[p%i]*(ll)(p-p/i)%p;
        for(int i=1;i<=min(n,m);i++)
            c[i]=(ll)i*inv[phi[i]]%p;
    }
    ll g(int x,int y)
    {
        ll res=0;
        for(int i=1;i<=min(x,y);i++)
            res=(res+(ll)mu[i]*(x/i)*(y/i))%p;
        return res;
    }
    int main()
    {
        int t;
        //freopen("input25.txt","r",stdin);
        //freopen("output25.txt","w",stdout);
        scanf("%d",&t);
        mobius_euler();
        while(t--)
        {
            scanf("%d%d%lld",&m,&n,&p);
            ans=0;
            init();
            for(int i=1;i<=min(n,m);i++)
                ans=(ans+c[i]*g(m/i,n/i))%p;
            printf("%lld
    ",ans);
        }
        return 0;
    }
    

    G. LYY双11剁手后的悲惨生活
    显然,偶数能够向前移动的最大位置 是 前一个偶数的后一个位置,奇数同理,那么可以维护两个队列,先将奇偶数分别放入两个队列里,最后答案就是不断输出两个队列的队首最小值。

    AC代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N=3e5+5;
    const int inf=0x3f3f3f3f;
    queue<int> q1,q2;
    char s[N];
    int ans[N],tot;
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            tot=0;
            scanf("%s",s+1);
            int n=strlen(s+1);
            for(int i=1;i<=n;i++)
            {
                char x=s[i];
                if(x&1) q1.push(x-'0'); //x&1 判断是否是奇数
                else q2.push(x-'0');
            }
            while(!q1.empty()&&!q2.empty())
            {
                if(q1.front()>q2.front())
                {
                    ans[++tot]=q2.front();
                    q2.pop();
                }
                else
                {
                    ans[++tot]=q1.front();
                    q1.pop();
                }
            }
            while(!q1.empty())
            {
                ans[++tot]=q1.front();
                q1.pop();
            }
            while(!q2.empty())
            {
                ans[++tot]=q2.front();
                q2.pop();
            }
            for(int i=1;i<=tot;i++)
                printf("%d",ans[i]);
            putchar('
    ');
        }
    	return 0;
    }
    

    AC代码

    H. 大佬的考验
    【改编自codeforces1234F】
    可参考另一篇博客:传送门

    I. 就决定是你了
    对Treecko 的每一个字符计数,求出所有字符中数量最小的(‘e’的数量要除以2);

    AC代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N=1e5+5;
    const int inf=0x3f3f3f3f;
    char s[N];
    int cnt[10];
    int main()
    {
    	scanf("%s",s);
    	int n=strlen(s);
    	for(int i=0;i<n;i++)
        {
            if(s[i]=='T') cnt[0]++;
            else if(s[i]=='r') cnt[1]++;
            else if(s[i]=='e') cnt[2]++;
            else if(s[i]=='c') cnt[3]++;
            else if(s[i]=='k') cnt[4]++;
            else if(s[i]=='o') cnt[5]++;
        }
        int ans=inf;
        cnt[2]>>=1; //cnt[2]/=2;
        for(int i=0;i<=5;i++) ans=min(ans,cnt[i]);
        printf("%d
    ",ans);
    	return 0;
    }
    

    J. wxy爱数电

    签到题,把二进制数转化成十进制数相加即可,但注意这是48位,会爆int,要开long long 存。

    AC代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N=55;
    int main()
    {
    	char s1[N],s2[N];
    	while(scanf("%s %s",s1,s2)!=EOF)
        {
            LL t1=0, t2=0;
        	int l1=strlen(s1),l2=strlen(s2);
    		for(int i=0;i<l1;i++)
    			t1<<=1,t1+=s1[i]-'0'; //等价于 t = 2 * t + s[i] - '0';
       		for(int i=0;i<l2;i++)
    			t2<<=1,t2+=s2[i]-'0';
    	
    		printf("%lld
    ",t1+t2);
        }
        return 0;
    	
    }

    K. 立方和问题

    【2019亚洲区域赛(徐州站 ) 铜牌题改编】
    a^3 + b^3 + c^3 = x , |a|,|b|,|c|<=5000 , 0<=x<=200
    这道题可以考虑枚举,-5000<=a,b,c<=5000

    最朴素的算法,枚举a,b,c ,判断立方和是否等于200以内的自然数, 复杂度O(n^3),显然超时。

    优化算法:a^3 + b^3 = x - c^3。枚举 a,b ,并把两数立方和存入 映射map或集合set(红黑树/AVL(BST)/hash表) 中,之后每输入x,就枚举c, 判断 map[ x+ c^3 ]是否等于1, 复杂度O(n^2*logn) ,外加一堆常数,并且map在数字多的情况下,运行效率大大退化,在一个小时内基本跑不出来。

    cqrt:立方根 ; ceil: 向上取整 ; floor: 向下取整

    打表: 同样的,我们枚举 a,b ,之后再枚举c=cqrt(x - a^3 - b^3 ),x∈[0,200] ,c需要枚举的范围实际上只有 [ floor( cqrt(- a^3 - b^3) ) , ceil( cqrt(200 - a^3 - b^3) ) ] ,而当a^3 或 b^3很大时,这个范围长度是一定 小于 cqrt(200) 的 ,因为200 对于 a^3 + b^3的值基本没有什么影响。

    当 a-> ∞ b->∞ , 这个范围的长度 -> 无穷小。再看三个数的立方和是否在 [ 0 , 200 ] ,若是 就把答案存起来,最后一起打印出来。

    所以 复杂度 O(n^2) ,并且常数是在可接受范围内的,大概十多秒就能跑出答案,
    我们把打出来的答案用一个小的数组存起来,之后所有询问直接 O(1) 输出数组里存的答案即可。

    当然在本地上打表,可以开O2 和 用 register

    打表代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int,int> pii;
    typedef pair<LL,LL> pLL;
    const int N=5e3;
    const double inf=0x3f3f3f3f;
    #define ls (i<<1)
    #define rs (i<<1|1)
    #define fi first
    #define se second
    #define mk make_pair
    #define pb push_back
    #define mem(a,b) memset(a,b,sizeof(a))
    #define debug puts("...")
    LL read()
    {
        LL x=0,t=1;
        char ch=getchar();
        while(!isdigit(ch)){ if(ch=='-')t=-1; ch=getchar(); }
        while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); }
        return x*t;
    }
    struct node
    {
        LL x,y,z;
        node(){ x=y=z=inf; }
        node(LL xx,LL yy,LL zz)
        {
            x=xx; y=yy; z=zz;
        }
    }ans[205];
    LL cnt=0;
    int main()
    {
        for(LL a=-N;a<=N;a++)
        {
            for(LL b=a;b<=N;b++)
            {
    
                LL l=-a*a*a-b*b*b,r=200-a*a*a-b*b*b;
                LL tl=l>=0?1:-1,tr=r>=0?1:-1;
                LL ll=tl*pow(abs(l),1.0/3),rr=tr*pow(abs(r),1.0/3);
               
                for(LL c=ll;c<=rr;c++)
                {
                    LL tmp=a*a*a+b*b*b+c*c*c;
                    if(tmp<=200&&tmp>=0)
                        ans[tmp]=node(a,b,c);//原题是输出解,所以这里把解存下来
                }
               
            }
        }
        for(int i=0;i<=200;i++)
            if(ans[i].x!=inf) printf("ans[%d]=YES
    ",i);//打印出答案,复制到一个新的程序, 提交并AC
            else printf("ans[%d]=NO
    ",i);
        return 0;
    }
    
  • 相关阅读:
    Java实现最大流量问题
    Java实现最大流量问题
    Java实现最大流量问题
    Java实现最大流量问题
    Java实现行列递增矩阵的查找
    Java实现行列递增矩阵的查找
    Java实现行列递增矩阵的查找
    Java实现行列递增矩阵的查找
    Java实现行列递增矩阵的查找
    通过QML Profiler分析程序性能问题
  • 原文地址:https://www.cnblogs.com/DeepJay/p/12025173.html
Copyright © 2020-2023  润新知