2016-2017 ACM-ICPC, NEERC, Southern Subregional Contest (Online Mirror, ACM-ICPC Rules, Teams Prefer

2016-2017 ACM-ICPC, NEERC, Southern Subregional Contest (Online Mirror, ACM-ICPC Rules, Teams Preferred)

话说这场比赛就是被小C叫过来水了一场啊...

然后就和机房里的人组了个队(一人rating撑起一片天).
这里写图片描述
然后就只写了7道题,虽然我只写了4道水题(I题没写出来真是可惜),并且WA了无数次.
这里写图片描述
但是至少A题我也提出了一点想法(虽然没什么用),至少有一点贡献.

比赛进程


比赛一开始,随机点开E题,然后读了读题目,感觉没什么,以为就是1到n一个个解锁过来,然后就以为是一道模拟题.

然而第二个样例并不能过,所以又重新读了读题,发现解锁的顺序是不确定的.

于是发现是一片新大陆…根本不会做.

然后发现好像对于两个队伍A,B,只有A先或者B先,而这两种对答案的贡献都是能算的,于是先算出来想dp一下,然后YY了一下,发现好像只要取大的就行了,应该没有冲突吧…

抱着沉重的心交了一发,于是就A掉了(获得成就:E题一血).此时已过半个小时.

看H题人蛮多,点开看看,好像不难,码了一会,过了样例就交了一发(打开疯狂提交的序幕),无情WA在了27组数据,原因是并没有看到只能够删除选定的文件,但是当时并没有发现,以为是不用理长度,所以去掉后又WA了一发,这次在第5组,那么就一定是我想错了吧,这时我依旧没有发现那个错误,已经快一个小时了.

再过一会,发现了,然后以为全问号是不行的,那么WA在了55组.

最后艰难地还是过了,WA了3次(居然还不是最多的).此时已过一个小时,同时队友也A掉了G题(WA了两次).

成功三题最后…

然后点开J题,刚刚看到一个物品有两个属性,队友点开了B题,问我这是什么东西,我看了看,交互题,”好吧,这题给我”,然后让他去写J题.

发现B题水的不行-_-,就和队友一起吃饭去了.

吃饭回来,已过一个半小时,一下子就码出了B题代码,交了一发,然后就见到了从未见到过的问题:
这里写图片描述
然后调了十来分钟,才发现是格式出错了,果然还是上一道交互题做的时间间隔太远了,都已经忘了怎么写交互题了…

这时过去了近两个小时.

然后就开始集全队之力开始攻克A题,因为这时已经有100+人过A题了而我们却还没过.

好,想着贪心却根本不知道如何来贪心,所以就很尴尬.

然后就出现了一个思路:

先拿最大的两个,如果次大的已经是最小的,那么看前面的选择中有没有可以放进这个最大的,如果可以就放进去,不然就把答案减一,这样就能够把最大的那个继续减下去了.

那么当我们把代码写出来后,发现RE了,检查一下数组,发现开小了囧…开大后又发现输出了一个全零的方案于是又WA了一遍,然后才A掉,现在已过三小时.

终于六题不是最后的了...

于是又分工,我打开了C题,他们开始一起搞I题(然而最后都没有写出来).

发现C题不是很难,对于所有的询问都BFS一遍,用堆来存买哪些,当消费小于有的钱时就成功了,不然返回-1.

然后这道题发生了很多很多尴尬的事,都不好意思说了…

首先没有看见有多少的需求,就一个个放进去,然后WA了很久,发现之后又WA了,发现有些事情没有处理好(可以无限买某种东西),于是又WA了无数遍…

WA了五次后终于A了,成功七题倒数(还不是我错的太多),分数从此定格,再也没有变过了…此时已过四个小时.

然后所有人就都在看I了,期间看了一下D,感觉不太像是能够写的,所以果断放弃.

开始各种脑补贪心,于是就走进了歪路,最后也没走出来(话说贪心好像能写),开始各种尝试,最后发现自己随手出的样例都能把自己程序卡掉后就很懵逼.

时间渐渐过去,却只能看自己的排名一个个的往下掉,很不爽的好吗!

头脑风暴了一个小时,最后还是没有写出来,最后91名结束.

最后发现I题其实用最小费用流就能够写了,还是学的太少了,还是Too Young Too Simple.

Solution:


虽然我们队A了7题(赛后我又把I题A了),但是我知道怎么写的就只有5道,所以就写一下吧.

A:Toda 2

Task:

给你一个序列,一次操作可以把2到5个数同时减一,一个数不能够被减到负数,但是可以被使用,最后使所有数都相同.问最后相同的数最大是多少,以及输出一种方案.

Solution:

首先有一个贪心的想法,每次都把两个最大的拿出来减1,同时固定一个下界,让数字不能够被减到这个数字以下.

那么当最大的两个都等于答案时就能够结束了,如果次大的等于答案,那么就在之前的方案中插入现在的最大值,如果不能够插进去,那么就让答案减1,让那个最大的值能够减小.

然后这道题就是模拟一下就行了.

#include<cstdio>
#include<string>
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 105
using namespace std;
int n,sum=0,ans=0;
struct node{
    int v,id;
    bool operator <(const node &a)const{
        return v<a.v;
    }
}A[M];
char str[10005][M];
priority_queue<node>Q;
int main(){
    scanf("%d",&n);
    int mi=M;
    for(int i=1;i<=n;i++){
        scanf("%d",&A[i].v),A[i].id=i-1;
        mi=min(mi,A[i].v);
        Q.push(A[i]);
    }
    int t=0;
    ans=mi;
    while(!Q.empty()){
        t++;
        node a=Q.top();
        Q.pop();
        node b=Q.top();
        Q.pop();
        if(ans<0)ans=0;
        if(a.v==ans&&b.v==ans)break;
        if(ans==0||(a.v>ans&&b.v>ans)){
            if(a.v)a.v--;
            if(b.v)b.v--;
            str[t][a.id]=str[t][b.id]='1';
            Q.push(a);
            Q.push(b);
        }else if(a.v>ans&&b.v==ans){
            int f=0;
            for(int i=1;i<t&&!f;i++)
                if(str[i][a.id]!='1'){
                    f=1,str[i][a.id]='1';
                    if(a.v)a.v--;
                    t--;
                }
            if(!f)ans--,t--;
            Q.push(a);
            Q.push(b);
        }
    }
    t--;
    for(int i=1;i<=t;i++){
        for(int j=0;j<n;j++)
            if(str[i][j]!='1')str[i][j]='0';
    }
    printf("%d\n%d\n",ans,t);
    for(int i=1;i<=t;i++)puts(str[i]);
    return 0;
}

B:Minimum and Maximum

Task:

[3n2]2 次询问中得到一个长度为n的序列的最大值和最小值,每次询问能够知道序列中两数的大小关系.

Solution:

还是一道水题吧,只用每次两两比较,大的加入大的里面,小的加入小的里面,然后依次两两比较就能够得到最大和最小了.

但是因为格式不对错了一次很不开心-_-,虽然没有扣分,但是就是不舒服…

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 55
using namespace std;
int winer[M],loser[M];
int main(){
    int cas;
    scanf("%d",&cas);
    while(cas--){
        int n;scanf("%d",&n);
        if(n==1){
            printf("! 1 1\n");
            fflush(stdout);
            continue;
        }
        int wsz=0,lsz=0;
        for(int i=1;i+1<=n;i+=2){
            printf("? %d %d\n",i,i+1);
            fflush(stdout);
            char S[5];scanf("%s",S);
            if(S[0]=='>'){
                winer[wsz++]=i;
                loser[lsz++]=i+1;
            }else {
                winer[wsz++]=i+1;
                loser[lsz++]=i;
            }
        }
        if(n&1){
            printf("? %d %d\n",loser[lsz-1],n);
            fflush(stdout);
            char S[5];scanf("%s",S);
            if(S[0]=='>'){
                loser[lsz-1]=n;
            }else if(S[0]=='<'){
                winer[wsz++]=n;
            }
        }
        int ans1,ans2;//ans1 small   ans2 big
        while(wsz>1){
            int nsz=0;
            for(int i=0;i+1<wsz;i+=2){
                printf("? %d %d\n",winer[i],winer[i+1]);
                fflush(stdout);
                char S[5];scanf("%s",S);
                if(S[0]=='>'){
                    winer[nsz++]=winer[i];
                }else winer[nsz++]=winer[i+1];
            }
            if(wsz&1){
                printf("? %d %d\n",winer[nsz-1],winer[wsz-1]);
                fflush(stdout);
                char S[5];scanf("%s",S);
                if(S[0]=='<')winer[nsz-1]=winer[wsz-1];
            }
            wsz=nsz;
        }
        ans2=winer[0];
        while(lsz>1){
            int nsz=0;
            for(int i=0;i+1<lsz;i+=2){
                printf("? %d %d\n",loser[i],loser[i+1]);
                fflush(stdout);
                char S[5];scanf("%s",S);
                if(S[0]=='<'){
                    loser[nsz++]=loser[i];
                }else loser[nsz++]=loser[i+1];
            }
            if(lsz&1){
                printf("? %d %d\n",loser[nsz-1],loser[lsz-1]);
                fflush(stdout);
                char S[5];scanf("%s",S);
                if(S[0]=='>')loser[nsz-1]=loser[lsz-1];
            }
            lsz=nsz;
        }
        ans1=loser[0];
        printf("! %d %d\n",ans1,ans2);
        fflush(stdout);
    }
    return 0;
}

C:Bulmart

Task:

给出一张图,有些点可以卖出一些东西,每个东西都有它自己的价格,现在给出q个询问,每个询问有st,ned,mone三个属性,表示你在st点,需要ned个物品,有money的钱,现在需要在每个城市订购物品,没有运费的存在,所以再远也没有关系,只要你付得起买东西的钱就行了,现在问最远的一个商店最近在哪里.

Solution:

说实话想到BFS就算了一下复杂度,发现没超,于是就开始敲了,然后就没看清题目数据范围,于是悲剧就开始了…

其实只要一直选现在最便宜的ned个物品,用堆来维护一下就可以了,当满足时就能够结束了.

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#define M 5005
#define LL long long
using namespace std;
void Rd(int &res){
    res=0;char p;
    while(p=getchar(),p<'0');
    do{
        res=(res<<1)+(res<<3)+(p^48);
    }while(p=getchar(),p>='0');
}
struct W{
    int v,cnt;
    bool operator <(const W &a)const{
        return v<a.v;
    }
};
vector<W>Q[M];
vector<int>edge[M];
int q[2][M],sz[2];
bool mark[M];
int query(int St,int Ned,int Mon){
    priority_queue<W>qq;
    LL cnt=0,val=0;
    memset(mark,0,sizeof(mark));
    sz[0]=sz[1]=0;
    int cu=0,nx=1,T=0;
    q[0][sz[cu]++]=St;
    mark[St]=1;
    while(sz[cu]){
        sz[nx]=0;
        for(int i=0;i<sz[cu];i++){
            int x=q[cu][i];
            int upp=Q[x].size();
            for(int l=0;l<upp;l++){
                W now=Q[x][l];
                if(cnt<Ned){
                    W in;
                    in.v=now.v;
                    in.cnt=min(1LL*now.cnt,Ned-cnt);
                    now.cnt-=in.cnt;
                    val+=1LL*in.cnt*in.v;
                    qq.push(in);
                    cnt+=in.cnt;
                }
                while(now.cnt){
                    W tp=qq.top();
                    if(tp.v>now.v){
                        if(tp.cnt<=now.cnt){
                            now.cnt-=tp.cnt;
                            val=val-1LL*tp.cnt*tp.v+1LL*now.v*tp.cnt;
                            qq.pop();
                            tp.v=now.v;
                            qq.push(tp);
                        }else {
                            qq.pop();
                            W in;
                            in.v=now.v,in.cnt=now.cnt;
                            qq.push(in);
                            tp.cnt-=now.cnt;
                            qq.push(tp);
                            val=val-1LL*now.cnt*tp.v+1LL*now.cnt*now.v;
                            now.cnt=0;
                        }
                    }else break;
                }
            }
            if(cnt==Ned&&val<=Mon)return T;
            int up=edge[x].size();
            for(int j=0;j<up;j++){
                int to=edge[x][j];
                if(!mark[to]){
                    mark[to]=1;
                    q[nx][sz[nx]++]=to;
                }
            }
        }
        cu=!cu,nx=!nx,T++;
    }
    return -1;
}
int main(){
    int n,m;
    Rd(n),Rd(m);
    for(int i=1;i<=m;i++){
        int x,y;Rd(x),Rd(y);
        edge[x].push_back(y);
        edge[y].push_back(x);
    }
    int t;Rd(t);
    for(int i=1;i<=t;i++){
        int x,y,z;
        Rd(x),Rd(y),Rd(z);
        Q[x].push_back((W){z,y});
    }
    int qu;Rd(qu);
    while(qu--){
        int x,y,z;
        Rd(x),Rd(y),Rd(z);
        printf("%d\n",query(x,y,z));
    }
    return 0;
}

E:Award Ceremony

Task:

给你一组分数,以及解锁后该分数的改变大小.问怎样改变解锁分数的顺序,能够使得过程中所有人排名的变化量最大.

Solution:

我们先考虑两两的比较先后,我们可以得到这两个分数先后解锁的贡献,然后取最大值加入答案中就行了,说实话我也不知道为什么,应该就是不会成环.

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 105
using namespace std;
struct W{
    int id,v,d;
    bool operator <(const W &a)const{
        if(v!=a.v)return v>a.v;
        return id<a.id;
    }
}Q[M];
int val[M][M];
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d %d",&Q[i].v,&Q[i].d),Q[i].id=i;
    int ans=0;
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++){
            W a=Q[i],b=Q[j],c=Q[i],d=Q[j];
            c.v+=c.d,d.v+=d.d;
            if(a<b&&b<c||b<a&&c<b)val[i][j]++;
            if(c<b&&d<c||b<c&&c<d)val[i][j]++;

            if(a<b&&d<a||b<a&&a<d)val[j][i]++;
            if(d<a&&c<d||a<d&&d<c)val[j][i]++;
            ans+=max(val[i][j],val[j][i]);
        }

    printf("%d\n",ans);
    return 0;
}

I:Olympiad in Programming and Sports

Task:

给你两个序列,从第一个序列中取s个数,第二个序列中取q个数,但是两个序列中的数的位置不能够相同,求所有取出的数的和的最大值.

Solution:

构图跑一下最小费用流就行了,从源点向每个点连一条流量为1的边,然后再从那些点分别连两条边到两个点,流量都为1,然后边权为上下两个序列的值的相反数,最后再从这两个点连流量分别为s和q的边到汇点就行了.

这样的题都写不出来,太水了TAT…

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#define M 3005
#define INF 1000000007
using namespace std;
struct W{
    int to,cap,co,id;
}tmp;
vector<W>edge[M];
void Add(int x,int y,int t,int v){
    edge[x].push_back((W){y,t,v,edge[y].size()});
    edge[y].push_back((W){x,0,-v,edge[x].size()-1});
}
int a[M],b[M],st,ed,dis[M];
int prev[M],nam[M];
bool mark[M];
bool SPFA(){
    queue<int>q;
    for(int i=0;i<=ed;i++)dis[i]=INF;
    dis[st]=0;
    mark[st]=1;
    q.push(st);
    while(!q.empty()){
        int now=q.front();q.pop();
//      printf("now:%d\n",now);
        mark[now]=0;
        int up=edge[now].size();
        for(int i=0;i<up;i++){
            W &op=edge[now][i];
            int ll=dis[now]+op.co,&to=op.to;
//          printf("to:%d %d %d\n",to,ll,dis[to]);
            if(op.cap>0&&ll<dis[to]){
                prev[to]=now;
                nam[to]=i;
                dis[to]=ll;
                if(!mark[to]){
                    q.push(to);
                    mark[to]=1;
                }
            }
        }
    }
    return dis[ed]!=INF;
}
int MFMC(){
    int re=0;
    while(SPFA()){
//      puts("_-");
        int max_flow=INF;
        for(int i=ed;i!=st;i=prev[i]){
            int &pl=edge[prev[i]][nam[i]].cap;
            if(pl<max_flow)max_flow=pl;
        }
        re+=1LL*dis[ed]*max_flow;
        for(int i=ed;i!=st;i=prev[i]){
            W &op=edge[prev[i]][nam[i]];
            op.cap-=max_flow;
            edge[op.to][op.id].cap+=max_flow;
        }
    }
    return re;
}
int main(){
    int n,s,p;
    scanf("%d %d %d",&n,&s,&p);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)scanf("%d",&b[i]);
    st=0,ed=n+3;
    Add(n+1,ed,s,0);
    Add(n+2,ed,p,0);
    for(int i=1;i<=n;i++)Add(st,i,1,0),Add(i,n+1,1,-a[i]),Add(i,n+2,1,-b[i]);
    printf("%d\n",-MFMC());
    int up=edge[n+1].size();
    for(int i=0;i<up;i++){
        W &op=edge[n+1][i];
        int to=op.to;
        if(to==n+3)continue;
        if(op.cap==1)printf("%d ",to);
    }putchar('\n');
    for(int i=0;i<up;i++){
        W op=edge[n+2][i];
        int to=op.to;
        if(to==n+3)continue;
        if(op.cap==1)printf("%d ",to);
    }putchar('\n');
    return 0;
}

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值