CF103C Russian Roulette

· · 题解

CF103C Russian Roulette

题目传送门

首先分析题意,我们可以得出三个性质:

  1. 如果最大程度上保证第一个人安全的话,第一个位置不要放子弹,不然上来就死了(除非所有的位置都要放子弹)。

  2. 让第二个人死的快点,那么需要隔一个放一个子弹 (从第二个位置开始放)。

  3. 如果 k>0最后一个位置一定放子弹,因为要保证字典序最小。

当当前的弹槽数为奇数时,由于性质3,易知最后一位一定是子弹,我们把最后一位删去,转换成偶数个,与偶数的情况一起讨论。

然后我们考虑两种情况:

然后记得开 long \ long ,不然亲测只能过五个点。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <cstdlib>

#define int long long
#define INF 0x3f3f3f3f

using namespace std;
int n,k,q; 
int read()
{
    register int x=0,y=1;
    register char c=getchar();
    while(c<'0'||c>'9') {if(c=='-') y=0;c=getchar();}
    while(c>='0'&&c<='9') { x=x*10+(c^48),c=getchar();}
    return y?x:-x;
}
bool check(int x,int n,int k)
{
    if(n&1) { //子弹隔一个放一个最优,如果是奇数,且 k > n/2 那么一定有两个相同的弹夹,
        if(x==n && k) //如果是最后一位 ,当前还有子弹,最后一位一定是子弹 
            return true;
        n--;//不是最后一位,最后一位放子弹字典序最小,先排除了
        k--; 
    }
    if(k-n/2>0)//子弹数 > 弹夹容量一半 一定有三个相邻的位置装子弹 
    {
        if(x%2==0) //偶数一定有子弹 
            return true;
        k-=n/2;//减去使用了的子弹 
        x=x/2+1;//重新编号 
        k=n/2-k;//剩下子弹从后依次排列,找到子弹排列的最前面一个的前一个位置 
        if(x>k) return true;//在后面的子弹序列里 
        return false;
    }
    else
    {
        if(x&1) return false;//奇数位置,一定没有子弹
        x=x/2;//筛去奇数,重新编号 
        k=n/2-k;//子弹序列前端的上一位置 
        if(x>k) return true;
        return false;
    }
}
signed main()
{
    n=read(),k=read(),q=read();
    while(q--)
    {
        int pot=read();
        if(check(pot,n,k))
        printf("X");
        else printf(".");
    }
    return 0;
}