zoukankan      html  css  js  c++  java
  • Codeforces 2018-2019 ICPC, NEERC, Southern Subregional Contest

    2018-2019 ICPC, NEERC, Southern Subregional Contest

    闲谈:

      被操哥和男神带飞的一场ACM,第一把做了这么多题,荣幸成为7题队,虽然比赛的时候频频出锅,差点被鸽,但还算打完了5h

      总的来说这场还是不算难的,7题还是少了点


    A

    题目:

      给出a,b,求出一个数满足是a的倍数,且数字和为b

    题解:

      男神懒得写博客就甩锅了

      直接宽搜,宽搜时队列中的状态保存为x,y,x表示当前的数%a==x,y表示数字和

      然后记录状态和方案就行了

    参考代码:

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    using namespace std;
    const int N=505,M=5010;
    struct node {
        int x,y;   // %A=x, 和为y , 上一个为k 
    } a[N][M],from[N][M],list[N*M*10]; int head,tail;
    bool v[N][M]; int ans[1000010];
    int main() {
        int A,B; scanf("%d%d",&A,&B);
        head=1; tail=2; list[1]=(node){0,0};
        memset(v,false,sizeof(v)); v[0][0]=true;
        while(head!=tail) {
            int x=list[head].x,y=list[head].y;
            for(int i=0;i<=9;i++) {
                int tx=(x*10+i)%A,ty=y+i;
                if(v[tx][ty]==true||ty>B) continue;
                v[tx][ty]=true;
                from[tx][ty]=(node){x,y};
                list[tail++]=(node){tx,ty};
            } head++; 
        }
        int len=0;
        int x=0,y=B;
        if(v[0][B]==false) { printf("-1
    "); return 0; }
        while(x!=0||y!=0) {
            int tx=from[x][y].x,ty=from[x][y].y;
            ans[++len]=y-ty; x=tx; y=ty;
        }
        for(int i=len;i>=1;i--) printf("%d",ans[i]);
        printf("
    ");
        return 0;
    }
    A(男神)

    B(*)

    题目:

      给出n个IPv4地址,表示n个区间

      形如a.b.c.d/x,相当于左端点为a*224+b*216+c*28+d,长度为232-x的区间(若形如a.b.c.d则与a.b.c.d/32相同)

      每个区间有颜色,黑区间或白区间,剩下没给出的地址中(也就是从0.0.0.0到255.255.255.255中没被覆盖的地址)非黑非白

      PS:这道题IPv4地址的定义是a.b.c.d/x中a*224+b*216+c*28+d的二进制位中后32-x位必须都是0

      求出最少的区间(以IPv4地址形式)能够覆盖所有黑区间(非黑非白的地址可以被覆盖,但是白区间不能被覆盖)

    题解:

      用01字典树来做

      首先按照给出的区间的左端点的二进制位从大到小x位插进字典树中,之所以只插x位是因为这段区间实际上就是以插进最后一位的所在节点的满子树(也就是所有点都有01孩子(深度不超过32))

      那我们就可以用一个节点来表示一段区间,并可以保证满足IPv4的定义

      然后我们对字典树上的点染色,黑色区间左端点插字典树的时候就将点值或1,白色就将点值或2

      那么整棵字典树上,显然点值为1的节点就表示这个点可以用来覆盖黑区间(因为不受白区间影响)

      我们就可以直接DFS求出最少的点来覆盖所有叶子节点,那肯定是找到的点深度越浅越好

      对于-1的情况,就提前排序判断一下区间重叠的情况就行了

    参考代码:

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    typedef long long LL;
    struct bw
    {
        LL l;int d,col;
    }p[210000];
    bool cmp(bw n1,bw n2){return n1.l<n2.l;}
    char st[31];
    void read(int x)
    {
        scanf("%s",st+1);
        int col;
        if(st[1]=='+') col=2;
        else col=1;
        int len=strlen(st+1);
        int b=24;
        LL l=0,r=0,d=0;
        for(int i=2;i<=len;i++)
        {
            if(st[i]=='.')
            {
                l+=d*(1LL<<b);
                b-=8;d=0;
            }
            else if(st[i]=='/')
            {
                l+=d*(1LL<<b);
                d=0;for(int j=i+1;j<=len;j++) d=d*10+st[j]-'0';
                break;
            }
            else
            {
                d=d*10+st[i]-'0';
                if(i==len) l+=d*(1LL<<b),r=l,d=32;
            }
        }
        p[x]=(bw){l,32-d,col};
    }
    struct trie
    {
        int c[2],col;
        trie()
        {
            col=0;
            memset(c,-1,sizeof(c));
        }
    }t[7100000];int tot;
    struct answer
    {
        LL d;int x;
    }ans[7100000];int cnt;
    void dfs(int x,LL k,int dep)
    {
        if(t[x].col==1)
        {
            ans[++cnt]=(answer){k,dep};
            return ;
        }
        if(t[x].c[0]!=-1) dfs(t[x].c[0],k,dep-1);
        if(t[x].c[1]!=-1) dfs(t[x].c[1],k+(1LL<<(dep-1)),dep-1);
    }
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++) read(i);
        sort(p+1,p+n+1,cmp);
        for(int i=1;i<n;i++)
        {
            if(p[i].col!=p[i+1].col&&p[i].l+(1LL<<p[i].d)-1>=p[i+1].l)
            {
                printf("-1
    ");
                return 0;
            }
        }
        tot=0;
        for(int i=1;i<=n;i++)
        {
            LL l=p[i].l;int d=p[i].d,col=p[i].col;
            int x=0;t[x].col|=col;
            for(int j=31;j>=d;j--)
            {
                int y=(l>>j)&1;
                if(t[x].c[y]==-1) t[x].c[y]=++tot;
                x=t[x].c[y];t[x].col|=col;
            }
        }
        cnt=0;dfs(0,0,32);
        printf("%d
    ",cnt);
        LL mk=(1<<8)-1;
        for(int i=1;i<=cnt;i++)
        {
            LL d=ans[i].d;
            printf("%lld.%lld.%lld.%lld/%d
    ",(d>>24)&mk,(d>>16)&mk,(d>>8)&mk,d&mk,32-ans[i].x);
        }
        return 0;
    }
    B

    C

    咕咕咕:

      本来可以提前2hA掉的题目,结果因为男神没开long long,主席树又爆空间,贡献13发罚时

      结果操哥直接就又写了一发扫描线过掉了,结果男神重开long long,也过了

      一题两种解法。。直接出门直走[飞机]


    D

    咕咕咕:

      男神一波甩锅[飞机]


    E(*)

    题目:

      给出n个任务,每个任务有它需要花费的时间,每做完m个任务就要休息等同于做完这m个任务所需要的时间的时间

      求出一个值d,表示只有花费的时间<=d的任务才会被完成

      在符合d的条件下,任务需要按照初始的顺序依次完成

      给出总时间t,要求做完任务的时间(包括休息时间)要<=t,若完成了最后想要完成的任务后需要休息时,可以不休息,但也不能再做任务了

      求出一个d,使得能做完的任务尽量多,求出最大可完成任务数和任意一个能得到最大可完成任务数的d值

    题解:

      一眼二分题(虽说是赛后才A的)

      先将原花费时间排序去重,然后二分位置,因为d值一定可以为其中一个任务的花费时间

      然后很容易想到实际上排序后每个任务的花费时间作为d值来求出的任务数,是呈单峰的

      那么我们就可以二分,然后对于前后的取值判断当前在单峰的哪个位置,然后再继续二分就好了

    参考代码:

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    typedef long long LL;
    int a[210000],s[210000];
    int n,m;LL t;
    int check(int x)
    {
        x=s[x];
        int ans=0,d=0;
        LL tt=0,t1=0;
        for(int i=1;i<=n;i++)
        {
            if(a[i]>x) continue;
            if(tt+a[i]>t) return ans;
            tt+=a[i];
            t1+=a[i];d++;
            ans++;
            if(d==m)
            {
                if(tt+t1>t) return ans;
                tt+=t1;
                t1=0;d=0;
            }
        }
        return ans;
    }
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d%lld",&n,&m,&t);
            for(int i=1;i<=n;i++) scanf("%d",&a[i]),s[i]=a[i];
            sort(s+1,s+n+1);int len=unique(s+1,s+n+1)-s-1;
            int l=1,r=len,sum=0,ans=1;
            while(l<=r)
            {
                int mid=(l+r)/2;
                int d=check(mid);
                if(d>sum) sum=d,ans=mid;
                if(check(mid+1)>d) l=mid+1;
                else r=mid-1;
            }
            printf("%d %lld
    ",sum,min(t,LL(s[ans])));
        }
        return 0;
    }
    E

     F

    题目:

      当前,有两位参与选拔的人Alice和Bob,有n个观众,每个观众有自己的影响力

      每个观众要么谁都不支持,要么支持Alice,要么支持Bob,要么两个都支持

      现在选拔现场的工作人员想要选出若干个观众来到现场(设人数为m),若a为支持Alice的人数,b为支持Bob的人数

      则现场的观众必须满足2a>=m且2b>=m,求出满足情况的条件下,能够请到的观众的影响力之和最大

    题解:

      裸裸的贪心,比赛的时候叫AKC验了想法,就直接做了

      首先对于两者都支持的观众一定可以选,因为他们都对a,b有贡献,而只会让m+1,所以无论什么情况都可以请

      然后用三个大根堆分别保存只支持Alice和只支持Bob和谁都不支持的影响力

      然后在两者都有值的情况下,分别取两个堆的堆顶加到答案里,因为这样会使得a+1,b+1,m+2,也是不影响答案的

      接下来对于剩下的一个堆,和谁都不支持的堆,显然接下来只能取m-2*(若剩下的堆支持Alice,则为b,若支持Bob,则为a)

      因为直到最后一步为止,a和b的值一直是相等的,所以对于哪个堆已经被取完,哪个堆的a或b就会成为约束条件

      然后将剩下的堆和谁都不支持的堆合起来取最大的m-2*(a或b)个影响力就行了

    参考代码:

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    using namespace std;
    typedef long long LL;
    char st[4];
    int id()
    {
        if(st[1]=='1')
        {
            if(st[2]=='0') return 1;
            else return 4;
        }
        else
        {
            if(st[2]=='1') return 2;
            else return 3;
        }
    }
    priority_queue<LL> A;
    priority_queue<LL> B;
    priority_queue<LL> C;
    int main()
    {
        int n;
        scanf("%d",&n);
        LL ans=0,a=0,b=0,m=0;
        for(int i=1;i<=n;i++)
        {
            LL d;
            scanf("%s%lld",st+1,&d);
            int p=id();
            if(p==1) A.push(d);
            if(p==2) B.push(d);
            if(p==3) C.push(d);
            if(p==4) ans+=d,m++,a++,b++;
        }
        while(A.empty()==0&&B.empty()==0)
        {
            ans+=A.top();
            ans+=B.top();
            a++;b++;m+=2;
            A.pop();B.pop();
        }
        if(A.empty()!=0&&B.empty()!=0)
        {
            while(2LL*a>=(m+1)&&C.empty()==0)
            {
                ans+=C.top();
                m++;C.pop();
            }
        }
        else
        {
            if(A.empty()==0)
            {
                LL d=2LL*a-m;
                while(d!=0)
                {
                    if(A.empty()!=0&&C.empty()!=0) break;
                    else if(A.empty()!=0&&C.empty()==0) ans+=C.top(),C.pop();
                    else if(C.empty()!=0&&A.empty()==0) ans+=A.top(),A.pop();
                    else if(A.top()>C.top()) ans+=A.top(),A.pop();
                    else ans+=C.top(),C.pop();
                    d--;
                }
            }
            else
            {
                LL d=2LL*a-m;
                while(d!=0)
                {
                    if(B.empty()!=0&&C.empty()!=0) break;
                    else if(B.empty()!=0&&C.empty()==0) ans+=C.top(),C.pop();
                    else if(C.empty()!=0&&B.empty()==0) ans+=B.top(),B.pop();
                    else if(B.top()>C.top()) ans+=B.top(),B.pop();
                    else ans+=C.top(),C.pop();
                    d--;
                }
            }
        }
        printf("%lld
    ",ans);
        return 0;
    }
    F

     G

    咕咕咕:

      因为是AKC的,所以甩锅,出门直走[飞机]

      最后一小时捡的漏


    H

    题目:

      给出n个模式串,有Q个询问,每个询问给出一个字符串,求出这个字符串是多少个模式串的子串,并求出任意一个模式串

    题解:

      签到题,直接字典树保存Q个询问的字符串,然后枚举每个模式串的每个子串,然后在字典树上跑,跑到尽头的时候将当前字典树的点权+1就行了

      然后判一下一个模式串中有多个相同子串的情况就行了

    参考代码:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<cstdlib>
    #include<queue>
    using namespace std;
    char st[11000][11];
    char a[11];
    struct trie
    {
        int c[41],s,t;
        trie()
        {
            memset(c,-1,sizeof(c));
        }
    }t[810000];int tot;
    int to[51000];
    int id(char cc)
    {
        if(cc>='0'&&cc<='9') return cc-'0'+1;
        if(cc>='a'&&cc<='z') return cc-'a'+1+10;
        return 37;
    }
    void bt(int p)
    {
        int x=0,len=strlen(a+1);
        for(int i=1;i<=len;i++)
        {
            int y=id(a[i]);
            if(t[x].c[y]==-1) t[x].c[y]=++tot;
            x=t[x].c[y];
        }
        to[p]=x;
    }
    int v[810000];
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%s",st[i]+1);
        int Q;
        scanf("%d",&Q);
        tot=0;
        for(int i=1;i<=Q;i++)
        {
            scanf("%s",a+1);
            bt(i);
        }
        memset(v,0,sizeof(v));
        for(int i=1;i<=n;i++)
        {
            int len=strlen(st[i]+1);
            for(int l=1;l<=len;l++)
            {
                int x=0;
                for(int r=l;r<=len;r++)
                {
                    int y=id(st[i][r]);
                    if(t[x].c[y]==-1) break;
                    x=t[x].c[y];
                    if(v[x]!=i)
                    {
                        v[x]=i;
                        t[x].s++;
                        t[x].t=i;
                    }
                }
            }
        }
        for(int i=1;i<=Q;i++)
        {
            printf("%d ",t[to[i]].s);
            if(t[to[i]].s==0) printf("-
    ");
            else printf("%s
    ",st[t[to[i]].t]+1);
        }
        return 0;
    }
    H

    I(*)

    咕咕咕:

      赛后甩锅大法好[飞机]


    J(*)

    咕咕咕:

      甩锅大法好[飞机]


    K

    题目:

      给出n个数,要分成连续的k段,使得每段的和相同

      若有合法分段的的情况就输出Yes并输出每一段的数的个数

      否则输出No

    题解:

      签到题,直接乱搞就行了,水题

    参考代码:

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    int a[110000];
    int ans[110000];
    int main()
    {
        int n,k;
        scanf("%d%d",&n,&k);
        int sum=0;
        for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum+=a[i];
        int tot=0;
        if(sum%k!=0) printf("No
    ");
        else
        {
            sum/=k;int d=0,cnt=0;
            int kk=0;
            for(int i=1;i<=n;i++)
            {
                d+=a[i];cnt++;
                if(d>sum){printf("No
    ");return 0;}
                if(d==sum)
                {
                    ans[++kk]=cnt;
                    cnt=0;d=0;
                }
            }
            printf("Yes
    ");
            for(int i=1;i<k;i++) printf("%d ",ans[i]);
            printf("%d
    ",ans[k]);
        }
        return 0;
    }
    K

    L(*)

    咕咕咕:

      究极甩锅[飞机]

  • 相关阅读:
    C#利用反射动态调用类及方法
    系统程序监控软件
    SQL server 2008 安装和远程访问的问题
    sql server 创建临时表
    IIS 时间问题
    windows 2008 安装 sql server 2008
    sql server xml nodes 的使用
    Window 7sp1 安装vs2010 sp1 打开xaml文件崩溃
    CSS资源网址
    Could not load type 'System.ServiceModel.Activation.HttpModule' from assembly 'System.ServiceModel, Version=3.0.0.0
  • 原文地址:https://www.cnblogs.com/Never-mind/p/9828450.html
Copyright © 2011-2022 走看看