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\n"); 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("\n");
    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\n");
            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\n",cnt);
    LL mk=(1<<8)-1;
    for(int i=1;i<=cnt;i++)
    {
        LL d=ans[i].d;
        printf("%lld.%lld.%lld.%lld/%d\n",(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\n",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\n",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("-\n");
        else printf("%s\n",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\n");
    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\n");return 0;}
            if(d==sum)
            {
                ans[++kk]=cnt;
                cnt=0;d=0;
            }
        }
        printf("Yes\n");
        for(int i=1;i<k;i++) printf("%d ",ans[i]);
        printf("%d\n",ans[k]);
    }
    return 0;
}
K

L(*)

咕咕咕:

  究极甩锅[飞机]

posted @ 2018-10-23 15:05  Star_Feel  阅读(364)  评论(0编辑  收藏  举报