Codeforces Round #573 (Div. 2) A B C D

题目链接:http://codeforces.com/contest/1191/problem


Problem A

看清题意就好

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<map>
#include<set>

using namespace std;

#define ll long long
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define for0(i,a,b) for (int i=a;i<b;i++)
#define pb push_back
#define fi first
#define se second
#define debug(x) printf("----Line %s----\n",#x)
#define pt(x,y) printf("%s = %d\n",#x,y)
#define INF 0x3f3f3f3f

const int N = 1e5+5;



int main()
{
    //int T;scanf("%d",&T);while (T--){}
    ll a;
    while (~scanf("%I64d",&a)){
        if (a%4==1){
            printf("0 A\n");
        }
        else if (a%4==3) printf("2 A\n");
        else if (a%4==2) printf("1 B\n");
        else printf("1 A\n");
    }
    return 0;
}

Problem B

凑出三张字母相同数字连续的牌或者三张相同的牌。

脑抽用了pair导致排序关键字的优先级出了点问题,难受

思路是先凑有无三张的,再凑有无两张的,注意4s6s这两张也可以

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<map>
#include<set>

using namespace std;

#define ll long long
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define for0(i,a,b) for (int i=a;i<b;i++)
#define pb push_back
#define fi first
#define se second
#define debug(x) printf("----Line %s----\n",#x)
#define pt(x,y) printf("%s = %d\n",#x,y)
#define INF 0x3f3f3f3f

const int N = 1e5+5;

struct node
{
    int first;
    char second;
    bool operator < (const node& a)const{
        if (se==a.se) return fi<a.fi;
        return se<a.se;
    }
    bool operator == (const node& a)const{
        return (fi==a.fi && se==a.se);
    }
}a[3];

int main()
{
    //int T;scanf("%d",&T);while (T--){}
    for0(i,0,3) scanf("%d%c",&a[i].fi,&a[i].se),getchar();
    sort(a,a+3);

    //for0(i,0,3) cout << a[i].fi << " " << a[i].se << endl;

    int ans = 2;
    if (a[1]==a[2] && a[2]==a[0]) ans = 0;
    else if (a[0].fi+1==a[1].fi && a[1].fi+1==a[2].fi && a[0].se==a[1].se && a[1].se==a[2].se) ans = 0;
    ///a0,a1
    else if (a[1]==a[2]||a[0]==a[1]) ans = 1;
    else if ((a[0].fi+1==a[1].fi && a[0].se==a[1].se )||(a[1].fi+1==a[2].fi && a[1].se==a[2].se )) ans = 1;
    else if ((a[0].fi+2==a[1].fi && a[0].se==a[1].se )||(a[1].fi+2==a[2].fi && a[1].se==a[2].se )) ans = 1;

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

Problem C

模拟整个删除情况。

用x表示总共删除的数的个数,初始化为0,nowk表示当前区间可以去掉的最大的数,初始化为k

对于给出的m个数排序,然后二分查找当前nowk部分可以去掉的数的个数nowx,

两种情况:

①nowx>x,说明在上一轮操作区间去掉了这部分区间含有m个数中的数后,腾出来空间前移,发现又有m个数中的数混到其中了,此时又要进行一次删除操作,操作次数+1

②nowx=x,说明在上一轮操作区间去掉了这部分区间含有m个数中的数后,腾出来空间前移,发现已经不包含m个数中的数了,此时,我们就计算一下  x + ?*K >= a[x+1]中这个?的值,表示k个k个的右移区间刚好可以把a[x+1]包进去,此种情况只是负责指向正确的区间,不增加操作次数。

 

如果已经找找了m个数,那么就break了。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<map>
#include<set>

using namespace std;

#define ll long long
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define for0(i,a,b) for (int i=a;i<b;i++)
#define pb push_back
#define fi first
#define se second
#define debug(x) printf("----Line %s----\n",#x)
#define pt(x,y) printf("%s = %d\n",#x,y)
#define INF 0x3f3f3f3f

const int N = 1e5+5;

ll a[N];

int main()
{
    //int T;scanf("%d",&T);while (T--){}
    ll n,m,k;
    scanf("%I64d %I64d %I64d",&n,&m,&k);

    for1(i,1,m) scanf("%I64d",a+i);
    sort(a+1,a+1+m);

    ll x = 0;
    ll nowk = k;
    ll ans = 0;
    while (nowk<=n){
        ll nowx = lower_bound(a+1,a+1+m,nowk)-a;
        if (nowk<a[1]) nowx = 0;///lower_bound一个坑点
        if (nowk>a[m]) nowx = m;
        if (nowk<a[nowx]) nowx--;///如果lowerbound找到的是大于的个数

        if (nowx>x) nowk = min(a[m],nowk+nowx-x),x=nowx,ans++;//,pt("nowx.1",nowx);///当前区间还有可以去掉的数
        else if (nowx==x && x<m) nowk = min(a[m],nowx+((a[x+1]-nowx-1)/k + 1)*k);//,pt("nowx.2",nowx);///转移到下一个有效区间
        else break;
    };
    printf("%I64d\n",ans);
    return 0;
}

比赛时出现的问题:

①lower_bound找寻大于等于要找的数的位置,我们却用来找小于等于的数量

几种情况都不是现在想要的:

(1)比最小值小,返回了end,特判改为0

(2)比最大值大,返回了end,特判改为m

(3)返回大于当前的位置,特判(a[nowx]==nowk),根据情况看nowx是否需要减一。

总之lower_bound要找小于等于的数的数量需要考虑这么几种情况。


Problem D

判断第一个人是否走完第一步就输的情况:

(1)0 0 .....

(2)....x-1,x,x

(3).....x,x.........y,y....

(4)......x,x,x......

如果第一个人成功走完一步活下来了,意味着场上不存在有两堆一样的,并且只要不是排好序后每堆的间隔都为1,那么当前的人取完之后仍然可以活下来。

那么最终取到最后一定会是0~n-1的全排列,到达这个状态的过程之前两个人完全不需要博弈,因为当前这个状态不会因为其中一个人取了特定的某个石头导致加快这个进程或者让另一个人提前暴毙。

到了这个状态之后,走下一步的人必输,我们只需判断谁走下一步即可。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<map>
#include<set>

using namespace std;

#define ll long long
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define for0(i,a,b) for (int i=a;i<b;i++)
#define pb push_back
#define fi first
#define se second
#define debug(x) printf("----Line %s----\n",#x)
#define pt(x,y) printf("%s = %d\n",#x,y)
#define INF 0x3f3f3f3f

const int N = 1e5+5;

int a[N];

int main()
{
    //int T;scanf("%d",&T);while (T--){}
    int n;
    a[0] = -2;///别忘了,很关键
    while (~scanf("%d",&n)){
    ll sum = 0;
    for1(i,1,n) scanf("%d",&a[i]),sum += (ll)a[i];
    sort(a+1,a+1+n);
    int cnt = 0;
    bool flag = false;
    bool losefirst = false;
    bool have_two_before = false;
    for1(i,1,n){
        flag = false;
        cnt = 1;
        if (a[i-1]==a[i]-1) flag = true;
        while (i+1<=n && a[i]==a[i+1]) i++,cnt++;
        if (a[i]==0 && cnt>=2){losefirst = true;break;}
        if (a[i]>0 && cnt==2){
            if (have_two_before){losefirst = true;break;}
            else have_two_before = true;
        }
        if (a[i]>0 && cnt==2 && flag){losefirst = true;break;}
        if (cnt>=3){losefirst = true;break;}
    }
    if (losefirst) puts("cslnb");///第二个人的名字
    else{
        //pt("laile",2);
        ll zuihou = (ll)n*(n-1)/2;
        ll bushu = sum-zuihou; //pt("bushu",bushu);
        if (bushu&1) puts("sjfnb");///第一个人的名字
        else puts("cslnb");///第二个人的名字
    }
    }
    return 0;
}

自我总结:

①lower_bound求小于等于考虑那三种情况

②代码不一定是按着你的思路走的,错在了比较前面的数据可以自己手写一组很普通的数据跑跑看。

③对边界状况处理能力有点欠缺或者到最后直接忘掉,emm

④这题博弈论的思路大概是:不是可以一堆取完加限制条件,所以跟sg无关。也不是反nim。那么找博弈过程的规律:尝试着判断最终状态。

⑤再次强调做题前先将遇到的问题分好大情况,判断是否能实现再动手

⑥掉了100,很爽

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值