Codeforces Round #721 (Div. 2)

题目:https://codeforc.es/contest/1527

 

A

题意:给定数字n。求m。使n&(n-1)&(n-2)&...&(m)=0,m是可能的最大值。

题解:转化为二进制可以发现,如果想要每一位都是0,那么必须有一个数最高位是0。设最高位为0,其他位为1的数为x,那么必然存在x到n之间的数使某个位&后为0。

  所以题目转化为求x。

#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<cmath>
#include<cstring>
 
using namespace std;
const int mx=50+10;
const int inf=1<<30;
typedef long long ll;
ll two[mx];
void init(){
    two[0]=1;
    for(ll i=1;i<=32;i++){
        two[i]=two[i-1]*2;
    }
}
void solve(){
    init();
    ll t;
    scanf("%lld", &t);
    for(ll i=1;i<=t;i++){
        ll v;
        scanf("%lld", &v);
        ll num =0;
        while(v!=0){
            v=v>>=1;
            num++;
        }
        printf("%lld\n", two[num-1]-1);
    }
 
}
int main(){
    solve();
    return 0;
}
View Code

 

B1. Palindrome Game (easy version)

题意:给定一个长为n的对称01串。alice和bob两个人轮流操作。如果当前状态为对称,那么必须选一个0位改为1;如果不对称,可以选一个0位改为1,也可以reverse一下,但是如果上一次操作对方以及reverse过了,那此次就不能reverse。

题解:首先,reverse对于最终结果没用,直接看成跳过操作。其次,由于是对称情况,所以串里面的1字符也没用。如果原来的串有x个0y个1,那么可以把原串看成x个0的串。

接下来分情况讨论:

如果x为偶数:

从x=2开始看,先手alice必须走一步(因为是对称),走完一步后局面不对称,bob可以跳过,alice不得不走下一步,游戏结束。此时先手alice比后手bob多走了两步。后手bob赢。

接着看x>2的情况。对于局面x,alice必须走一步,bob可以走alice对称的位子,从而局面转化为了x-2的对称局面;接着alice不得不因为对称继续走一步,bob走对称位子,局面转化为x-4;...;

以此类推,一直持续到局面2,此时alice走了之后bob可以不走。因此最后局面是 alice比bob多走两步。后手bob赢。

如果x为奇数:

从x=1开始看,先手alice走一步,结束;alice输;

从x>3开始看,先手alice必走一步,走哪一步?:

  如果走正中间mid位置,局面又转化为了没结束对称局面,此时对称下先手为bob,后手为alice。那么最终alice多走了开始的第一步,但是对称局面下可以少走两步。最终情况alice少走一步。alice赢。

 

总结:

x为偶数,后手必赢

x为奇数。如果x为1,后手必赢;反之先手必赢。

#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<cmath>
#include<cstring>

using namespace std;
const int mx=1e3+10;
const int inf=1<<30;
typedef long long ll;

void solve(){
    ll n;
    scanf("%lld", &n);
    for(ll i=1;i<=n;i++){
        ll v;
        scanf("%lld", &v);
        char s[mx];
        scanf("%s", s);
        for(ll j=0;j<strlen(s);j++){
            if(s[j]=='1')v--;
        }
        if(v&1){
            if(v == 1){
                printf("BOB\n");
            }
            else{
                printf("ALICE\n");
            }
        }
        else{
            printf("BOB\n");
        }
    }
}
int main(){
    solve();
    return 0;
}
View Code

 

B2. Palindrome Game (hard version)

题意:B1上加了条件 开局可能不对称

题解:

离谱,上次写的没保存。。。不想再写一遍了。

也是分奇偶。

偶数n。先手必赢。

奇数n。只有一种情况:正中间的值为0,然后存在其他一个位置的值为0。此时平局,其他情况先手必赢。

#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<cmath>
#include<cstring>

using namespace std;
const int mx=1e3+10;
const int inf=1<<30;
typedef long long ll;
const int mx2=30;
char alice[mx2]="ALICE";
char bob[mx2]="BOB";
char draw[mx2]="DRAW";
ll n, v, num;
char ch[mx];

ll duichen(ll len){//对称情况下 如果先手必胜 返回true 反之返回false
    if(len&1){//奇数 //如果不为1 先手必胜 至少 少走1步
        return len == 1?-1:1;
    }
    return -1;//偶数情况下 后手 bob必胜 至少 少走2步
}
ll chulijishu(){//0 平局
    ll res=0;//对称的0的个数 也就是对称局面剩下的步数
    //只要res不为0 对称的后手可以比先手少走两步
    ll need =0;//need代表 实现对称 至少需要多少步数(不考虑正中间
    ll mid=(v-1)/2;
    for(ll i=0, j=v-1;i<j;i++, j--){
        if(ch[i]!=ch[j]) need++;
        if(ch[i]==ch[j] && ch[i]=='0') res+=2;
    }
    if(ch[mid] == '0'){
        res++;
    }
    if(need == 0){//如果局面对称
        return duichen(res);
    }
    //局面不对称 剩下的 也没有对称的情况
    if(res == 0){//alice躺赢
        return 1;
    }
    //局面不对称 剩下的也有对称的情况
    //1: need == 1 res == 1
    if(need == 1 && res == 1){
        //res == 1 说明是 mid位置 有一个0
        //need == 1 说明其他位置 还有一个0
        return 0;//平局
    }
    return 1;
//    //2: need != 1 res == 1
//    else if( need != 1 && res == 1){
//        //res == 1 说明是 mid位置 有一个0
//        //最后两步 肯定是 一个mid 一个其他 平局 所以看之前的步数决定
//        //因为need!=1 所以b的步数肯定大于a
//        return 1;//a躺赢
//    }
//    //3: need == 1 res != 1
//    else if(need == 1 && res != 1){
//        //need == 1 说明除了mid 其他位置px 还有一个0
//        //a走了px 构成对称 res!=1 那么 b必走2步
//        //最后a少走一步
//        return 1;
//    }
//    //4: need != 1 res != 1
//    else if(need != 1 && res != 1){
//        //a可以一直躺到对称前 那么最差的情况是 b走了一步 a也走了一步
//        //res!=1  b被迫走两步
//        return 1;
//    }
}
ll chulioushu(){
    ll res=0;//对称的0的个数 也就是对称局面剩下的步数
    //只要res不为0 对称的后手可以比先手少走两步
    ll need =0;//need代表 实现对称 需要多少步数(和奇数不一样
    //v代表总长度
    for(ll i=0, j=v-1;i<j;i++, j--){
        if(ch[i]!=ch[j]) need++;
        if(ch[i]==ch[j] && ch[i]=='0') res+=2;
    }
    if(need == 0){//如果局面对称
        return duichen(res);
    }
    //局面必然是不对称的
    if(res == 0){//说明不存在对称的部分 alice可以躺赢
        return 1;
    }
    //如果存在对称的部分 alice就需要去抢 对称前的 前一步
    //alice 至少可以少 2-1 = 1步 所以alice必赢
    return 1;
}
void solve(){
    scanf("%lld", &n);
    for(ll i=1;i<=n;i++){
        num=0;
        scanf("%lld", &v);
        scanf("%s", ch);
        ll num;
        if(v&1){
            num = chulijishu();
        }
        else{
            num = chulioushu();
        }
        if(num == 1)printf("%s\n", alice);
        else if(num == -1) printf("%s\n", bob);
        else printf("%s\n", draw);
    }
}
int main(){
    solve();
    return 0;
}
View Code

 

C. Sequence Pair Weight

题意:一个长为n的串A。对于一个子串a,里面的a[i]=a[j]的ij对数(i<j)为这个子串的weight。求这个串A的所有子串的weight之和。

题解:

把题目拆为求每种数字对结果的贡献。

以1211为例,先看1给最后结果带来的贡献。把从左到右的三个1称为一号1二号2三号1

从右往左看,三号1为i的贡献为0。(注意 i<j)

再看二号1,以二号1开头,它可以和三号1构成最多贡献1(因为三号1之后没有其他位置了)。对于以二号1左边的开始的串,每个串可能有的二号1和三号1的组合贡献也是1。所以二号1为i的贡献为3*1。3为包括二号1在内的二号1之前的开头的可能,1为二号1开头作为i带来的贡献。

同理看一号1。以一号1开头,可以和三号1构成最多贡献1(因为三号1之后没有其他位置了)。可以和二号1构成最多贡献2(因为包括二号1及以后一共两个位置)。那么以二号1开头作为i的贡献是1+2=3;而包括一号1在内的开头的可能只有一种。所以一号1作为i带来的贡献是1*(1+2);

然后整理下,可以发现每个1作为i带来贡献是(包括当前1位置及之前的位置的个数)*(当前1位置作为开头和右边的1的贡献和)。

(包括当前1位置及之前的位置的个数)很容易求得。

而(当前1位置作为开头和右边的1的贡献和)可以发现

     三号号1贡献和0

  二号1贡献和2

  一号1贡献和1+2

  。。

其实可以发现,i号1的贡献和是i+1的贡献和加上 (n-(i+1)号1的位置+1)

然后就可以算答案了。

绕死我了这题。。= =。

#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<cmath>
#include<cstring>
using namespace std;
const int mx=1e5+10;
const int inf=1<<30;
typedef long long ll;
ll num, v;
void solve(){
    ll n;
    scanf("%lld", &n);
    for(ll t=1;t<=n;t++){
        scanf("%lld", &num);
        map<ll,ll>mp;//mp[i]表示数字i的最右边的位置是啥
        ll bef[mx];//bef[i]表示i位置上的数字的左边的第一个相同数字位置是啥
        //    memset(bef, 0, sizeof(bef));
        for(ll i=1;i<=num;i++){
            scanf("%lld", &v);
            bef[i]=mp[v];
            mp[v]=i;
        }
        map<ll,ll>::iterator it;
        ll ans=0;
        for(it=mp.begin();it!=mp.end();it++){
            ll val=it->first;
            ll rig=it->second;//当前数的右边第一个相同数位置
            ll cur=bef[rig];//右边开始数第二个位置
            ll tot=0;//目前为止 和右边开始数第一个、第二个。。。可以构成的数量
            while(cur!=0){
                //cur是当前位置
                tot=tot+(num-rig+1);
                //处理一下带来的增量
                ans+=tot*cur;
                rig=cur;
                cur=bef[cur];
            }
        }
        printf("%lld\n", ans);
    }
}

int main(){
    solve();
    return 0;
}
View Code

 

D. MEX Tree

题意:给定一个n个节点的树,节点值为0到n-1各不重复。每一次求节点i和节点j的对数,使i到j的最短路径上的节点值的mex值为k。一共求k=0~n的对应对数。

题解:

https://codeforc.es/blog/entry/90939

直接翻官方题解了。。。555不会。

 

 

 

 

 

 

posted @ 2021-05-21 20:09  反射狐  阅读(51)  评论(0编辑  收藏  举报