zoukankan      html  css  js  c++  java
  • CCPC2018-湖南全国邀请赛 解题报告 Apare

    CCPC2018-湖南全国邀请赛 解题报告

    by lj zx xzc 2019/5/6


    题目链接:

    vj链接:CCNUACM Team Contest 2019 Round #5
    HDU链接:CCPC2018-湖南全国邀请赛-重现赛(感谢湘潭大学)

    A. Easy h-index

    题意:

    我们的代码:

    /*
    Status
    Accepted
    Time
    46ms
    Memory
    2156kB
    Length
    337
    Lang
    G++
    Submitted
    2019-05-05 15:16:41
    */
    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    int a[200005];
    int main()
    {
    	int n,k;
    	long long sum;
    	while(scanf("%d",&n)!=EOF){
    		sum = 0;
    		for(int i = 0;i <= n;i++)
    		scanf("%d",&a[i]);
    		for(int i = n;i >=0;i--){
    			sum+=a[i];
    			if(sum>=i){
    				printf("%d
    ",i);
    				break;
    			} 
    		}
    	}
    	return 0;
    } 
    

    B. Higher h-index

    题意:

    我们的代码:

    /*
    Status
    Accepted
    Time
    15ms
    Memory
    1224kB
    Length
    203
    Lang
    G++
    Submitted
    2019-05-05 17:02:07
    */
    #include<stdio.h>
    #include<algorithm>
    using namespace std;
    #define ll long long
    ll n,a;
    int main()
    {
        while(scanf("%lld%lld",&n,&a)!=EOF)
        {
            printf("%lld
    ",(n+a)/2);
        }
        return 0;
    }
    

    C. Just h-index

    题意:
      数组大小1E5,元素1到1E5,询问1E5,每个询问给出一个区间,要找到最大的h,使得这个区间内>=h的数有>=h个

    思路:
      二分答案,用主席树求区间第K大(len+1-k小),然后check xx>=mid 即可
      据说主席树自带二分性质
    我们的代码:

    /*
    Status
    Accepted
    Time
    1357ms
    Memory
    22948kB
    Length
    1889
    Lang
    G++
    Submitted
    2019-05-05 17:45:53
    */
    #include<bits/stdc++.h>
    #define For(i,a,b) for(register int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(register int i=(a);i>=(b);--i)
    #define Mst(a,b) memset(a,(b),sizeof(a))
    #define LL long long
    using namespace std;
    const int maxn = 1e5+20;
    int a[maxn],n,cnt;
    struct Node{
        int Lchild,Rchild,sum;
    }node[maxn*20];
    int root[maxn*20];
    int update(int left,int right,int v,int p)
    {
        if(v<left||v>right) return p;
        int t = cnt++;
        node[t].Lchild = node[p].Lchild;
        node[t].Rchild = node[p].Rchild;
        node[t].sum = node[p].sum+1;
        int mid = (left+right)>>1;
        if(left==right) return t;
        node[t].Lchild = update(left, mid,  v,node[p].Lchild);
        node[t].Rchild = update(mid+1,right,v,node[p].Rchild);
        return t;
    }
    void build()
    {
        cnt = 0;
        root[0] = cnt++;
        node[0].Lchild = node[0].Rchild = node[0].sum = 0;
        For(i,1,n) root[i] = update(1,n,a[i],root[i-1]);
    }
    int query(int left,int right,int k,int rx,int ry)
    {
        if(left==right) return left;
        int tot = node[node[ry].Lchild].sum - node[node[rx].Lchild].sum;
        int mid = (left+right)>>1;
        if(k<=tot) return query(left,   mid,k,    node[rx].Lchild,node[ry].Lchild);
        else return       query(mid+1,right,k-tot,node[rx].Rchild,node[ry].Rchild);
    }
    
    int main()
    {
        //freopen("in.txt","r",stdin);
        int q;
        while(scanf("%d%d",&n,&q)!=EOF)
        {
            For(i,1,n) scanf("%d",a+i);
            build();
            int h,l,r;
            while(q--)
            {
                scanf("%d%d",&l,&r);
                int left = 1,right = r-l+2,mid;
                while(right-left>1)
                {
                    mid = (left+right)>>1;
                    int xx = query(1,n,r-l+2-mid,root[l-1],root[r]);
                    //printf("[%d,%d]之间的第%d小是%d
    ",l,r,n+1-mid,xx);
                    if(xx>=mid) left = mid;
                    else right = mid;
                }
                printf("%d
    ",left);
            }
        }
    
    
        return 0;
    }
    
    

    F. Sorting

    题意:
      结构体排序,字典序最小,且a0+b0/a0+b0+c0 < a1+b1/a1+b1+c1
    直接乘会爆Long long,可以优化一下,见代码

    我们的代码:

    /*
    Status
    Accepted
    Time
    46ms
    Memory
    1484kB
    Length
    1051
    Lang
    G++
    Submitted
    2019-05-05 15:35:11
    */
    #include<bits/stdc++.h>
    #define For(i,a,b) for(register int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(register int i=(a);i>=(b);--i)
    #define Mst(a,b) memset(a,(b),sizeof(a))
    #define LL long long
    using namespace std;
    const int maxn = 1e5+20;
    struct Node{
        unsigned long long a,b,c;
        int id;
        bool operator < (const Node& r)const{
            if(c*(r.a+r.b+r.c)==r.c*(a+b+c))
                return id < r.id;
            return c*(r.a+r.b+r.c)>r.c*(a+b+c);
        }
        void input(int _id)
        {
            id = _id;
            //scanf("%ull%ull%ull",&a,&b,&c);
            cin>>a>>b>>c;
        }
        void out()
        {
            printf("node[%d] %ull %ull %ull
    ",id,a,b,c);
        }
    }node[maxn];
    int main()
    {
        //freopen("in.txt","r",stdin);
        int n;
        while(scanf("%d",&n)!=EOF)
        {
            For(i,1,n)
            {
                node[i].input(i);
               // node[i].out();
            }
            sort(node+1,node+1+n);
            printf("%d",node[1].id);
            For(i,2,n)
            {
                printf(" %d",node[i].id);
            }
            printf("
    ");
        }
    
    
        return 0;
    }
    

    G. String Transformation

    题意:
      有两个字符串仅仅由’a’,‘b’,'c’组成,我们对每个字符串都可以插入或者删除"aa",“bb”,“abab”,问给定的两个字符串是否可以变为一样的

    样例:
    Sample Input
    ab
    ba
    ac
    ca
    a
    ab
    Sample Output
    Yes
    No
    No
    
    分析:

      由样例我们可以知道ab和ba可以相互转换,就是说相邻的ab可以交换:

    • ab->aababb->ba
    • ba->aababb->ab
      而c是不可以变的,所以我们以c为坐标即可
      我们可以知道,插入和删除的字符串长度为2和4,所以不改变原串长度的奇偶性。
    • 如果两个串长度奇偶性不同,直接输入no
    • 然后我们对两个串分别遍历,记录下来每个串c的位置,如果c的个数不同,直接输出no
    • 我们可以把字符串开头结尾都插入一个’c’,然后两个串比较任意连个相邻的c之间a和b的个数的奇偶性是否相同即可(见代码)

    我们的代码:

    /*
    Status
    Accepted
    Time
    15ms
    Memory
    1412kB
    Length
    1561
    Lang
    G++
    Submitted
    2019-05-05 16:57:45
    */
    #include<bits/stdc++.h>
    #define For(i,a,b) for(register int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(register int i=(a);i>=(b);--i)
    #define Mst(a,b) memset(a,(b),sizeof(a))
    #define LL long long
    using namespace std;
    const int maxn = 1e5+20;
    char s[maxn],t[maxn];
    int lens,lent;
    int ps[maxn],cnts;
    int pt[maxn],cntt;
    bool ok(int sL,int sR,int tL,int tR)
    {
        int nb=0,na=0;
        For(i,sL,sR)
        {
            if(s[i]=='a') ++na;
            else if(s[i]=='b') ++nb;
        }
        For(i,tL,tR)
        {
            if(t[i]=='a') --na;
            else if(t[i]=='b') --nb;
        }
        na = abs(na);
        nb = abs(nb);
        return (na%2==0&&nb%2==0);
    }
    int main()
    {
        //freopen("in.txt","r",stdin);
        while(scanf("%s%s",s,t)!=EOF)
        {
            lens = strlen(s);
            lent = strlen(t);
            cnts = cntt = 0;
            ps[cnts++] = -1;
            pt[cntt++] = -1;
            if((max(lent,lens)-min(lent,lens))%2)
            {
                printf("No
    ");continue;
            }
            for(int i=0;i<lens;++i)
            {
                if(s[i]=='c') ps[cnts++] = i;
            }
            for(int i=0;i<lent;++i)
            {
                if(t[i]=='c') pt[cntt++] = i;
            }
            if(cnts!=cntt)
            {
                printf("No
    ");continue;
            }
            ps[cnts++] = lens;
            pt[cntt++] = lent;
            bool flag = true;
            For(i,1,cntt-1)
            {
                if(!ok(ps[i-1]+1,ps[i]-1,pt[i-1]+1,pt[i]-1))
                {
                    printf("No
    ");
                    flag = false;
                    break;
                }
            }
            if(flag) printf("Yes
    ");
        }
        return 0;
    }
    

    K. 2018

    题意:
      给两个区间, [a,b]和[c,d], 从第一个区间里挑一个数x,从第二个区间里挑一个数y,使得x*y是2018的倍数,问有多少种取法

    分析:
      分解一下质因数,我们知道2018 = 2 * 1009,两个质数,我们可以简单容斥一下
      一个区间[a,b]内k的倍数的个数为:b/k-(a-1)/k
    我们令f(a,b,k) = b/k-(a-1)/k
      设A,B为集合,则A - B = A - AB

    我们就先分析[a,b]这个区间选出的数x:

    1. x只是2的倍数: y2n9 = f(a,b,2) - f(a,b,2018)
    2. x只是1009的倍数:n2y9 = f(a,b,1009) - f(a,b,2018)
    3. x是2018的倍数:y2y9 = f(a,b,2018)
    4. x既不是2的倍数,又不是1009的倍数: n2n9 = (b-a+1)-(y2n9+n2y9+y2y9)
    • 然后x只是2的倍数,那么y是1009的倍数即可:f(c,d,1009)
    • x只是1009的倍数, y是2的倍数即可:f(c,d,2)
    • x是2018的倍数,y任意取 c-d+1
    • x既不是2也不是1009的倍数,那么y必须是2018的倍数:f(c,d,2018)
      对应相乘相加即可(注意会爆int)

    我们的代码:

    /*
    Status
    Accepted
    Time
    15ms
    Memory
    1384kB
    Length
    951
    Lang
    G++
    Submitted
    2019-05-05 14:58:52
    */
    #include<bits/stdc++.h>
    #define For(i,a,b) for(register int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(register int i=(a);i>=(b);--i)
    #define Mst(a,b) memset(a,(b),sizeof(a))
    #define LL long long
    using namespace std;
    int f(int left,int right,int xx)
    {
        return right/xx-(left-1)/xx;
    }
    struct Node
    {
        int y2y9,y2n9,n2n9,n2y9;
        void init(int left,int right)
        {
            y2y9 = f(left,right,2018);
            y2n9 = f(left,right,2)-f(left,right,2018);
            n2y9 = f(left,right,1009)-f(left,right,2018);
            n2n9 = right-left+1-(y2n9+n2y9+y2y9);
        }
        void getAns(int L,int R)
        {
            LL res = 0;
            res += 1ll*n2n9*f(L,R,2018);
            res += 1ll*y2n9*f(L,R,1009);
            res += 1ll*n2y9*f(L,R,2);
            res += 1ll*y2y9*(R-L+1);
            printf("%lld
    ",res);
        }
    }xx;
    
    int main()
    {
        int a,b,c,d;
        while(scanf("%d%d%d%d",&a,&b,&c,&d)!=EOF)
        {
            xx.init(a,b);
            xx.getAns(c,d);
    
        }
        return 0;
    }
    

    J. Vertex Cover(没出)

    题意:
      现在有n个点构成的完全图,编号为0到n-1,第i个点的权重为2^i
      现在Alice从完全图里面选了若干条边,然后聪明的Bobo选了一些点来覆盖这些边。只要边的一个端点被选择就说这条边被覆盖了。Bobo选的点集是权重最小的。
      现在用二进制的形式给出Bobo选择的点集,现在问Alice可能选择的边集的个数
    分析:
      由于点的权重是2^i,就是说这个点之前所有点的权重加起来都没有它大。所以我们如果已知边选点,肯定是贪心地选择下标小的点。
      就是说,如果我们选择了一个点V,那么必定至少存在一条边,边的一个端点是这个点V,另一个端点是下标比V大的点,那么Bobo才有选择这个点的理由

    思路:

    • 按下标从大到小遍历点集中的点
    • 每个点x从点集外下标比自己的点中至少要连一条边(2^k-1),k = (n-x)-(sum[n]-sum[x)
    • 点集中每个点x和点集内下标比自己的点的边可连可不连(2^(x-1))
    • 遍历一遍相乘即可

    我们的代码:

    /*
    Status
    Accepted
    Time
    31ms
    Memory
    2744kB
    Length
    1124
    Lang
    G++
    Submitted
    2019-05-06 10:42:37
    */
    #include<bits/stdc++.h>
    #define For(i,a,b) for(register int i=(a);i<=(b);++i)
    #define Rep(i,a,b) for(register int i=(a);i>=(b);--i)
    #define Mst(a,b) memset(a,(b),sizeof(a))
    #define LL long long
    using namespace std;
    const int mod = 1e9+7;
    const int maxn = 1e5+20;
    int n;
    char s[maxn];
    int a[maxn]; ///a[i] = 1 代表第i个点被选择了
    int sum[maxn];
    LL fast_pow(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()
    {
        //freopen("in.txt","r",stdin);
        while(scanf("%d%s",&n,s+1)!=EOF)
        {
            vector<int> v;
            int len =strlen(s+1);
            int cnt = 0;
            for(int i=len;i>0;--i)
            {
                a[++cnt] = s[i]-'0';
                if(s[i]=='1') v.push_back(cnt);
            }
            For(i,cnt+1,n) a[i] = 0;
            sum[0] = 0;
            For(i,1,n)
                sum[i] = sum[i-1]+a[i];
            LL ans = 1;
            for(auto &x:v)
            {
                ans = ans*fast_pow(2ll,x-1)%mod;
                ans = ans*((fast_pow(2ll,n-x-sum[n]+sum[x])-1+mod)%mod)%mod;
            }
            printf("%lld
    ",ans);
        }
        
        return 0;
    }
    
  • 相关阅读:
    CentOS7 时间设置与网络同步
    CentOS7 系统升级,删除centos7开机界面多余选,升级至最新的内核
    Docker 编排工具Rancher 2.0
    Docker 编排工具Rancher 1.6.18
    通过WSL在Windows下安装子Linux系统
    Docker 使用Dockerfile构建redis镜像
    初探PHP多进程
    nginx转发
    mime类型
    socket php
  • 原文地址:https://www.cnblogs.com/Apare-xzc/p/12243645.html
Copyright © 2011-2022 走看看