码迷,mamicode.com
首页 > 其他好文 > 详细

CTYZ信心赛T5 题解

时间:2018-12-27 18:40:07      阅读:131      评论:0      收藏:0      [点我收藏+]

标签:一个   cst   while   其他   code   ||   解决   scanf   iostream   

吐槽ing:

一道有趣的二进制题

注意加粗部分是限制条件

我们先考虑暴力分\(40\)分:

首先那个\(Fight\)值一看就知道是二进制。

对于这个暴力分,应该是一种很暴力(废话)的解法,我们直接从\(b\)\(a\)枚举,然后判断这一个数合不合法,如果合法,就+1,直到找到第\(k\)大,输出答案即可。

考虑\(100pts:\)

这里我们就要开始讨论二进制算法了。

首先我们考虑巨佬站队方式的限制,对于每个询问\([a,b]\)假设巨佬的\(Fight\)值是\(p\),若\(p>b\),那区间\([b,p]\)就没有作用了,我们的查询区间就直接缩成了\([a,min_{b,p-1}]\),这一个限制条件就解决了。

我们再考虑巨佬的站队对蒟蒻的约束作用(即限制了某些位必须是0):

首先我们假设没有这个条件,很显然的,如果第\(i\)大的站队方式的\(Fight\)值是\(j\),那么第\(i+1\)大的站队方式的\(Fight\)值就是\(j-1\)(二进制转十进制),

接着我们再考虑存在某些位必须是\(0\)的情况:

这里的方法是把所有有约束条件的\(0\)删去,得到一个新的,没有约束条件的数。

比如这里有一个第\(i\)大的数的二进制数,其中所有\(0\)都是被限制了的

1 0 1 0 1 0

我们把所有被限制的0删去,得到一个没有被限制的数:

1 1 1

显然这个\(Fight\)值是7,求第\(i+1\)大的数时,我们将7-1,得到6.

1 1 0

然后,我们再把刚才去掉的0加回去

1 0 1 0 0 0

这就是我们要找的数。

关于删除操作,我是这样做的,假设我们要删除下面这个二进制的第3个数:

1 1 1 1 1 1
      ^

我们先把后2位取下来:

1 1 1 1 0 0
s = 1 1

再删除第3位:

1 1 1 0 0 0

整体右移一位,在把\(s\)搬回去。

插入也是同理(改成左移)

具体代码实现(时间复杂度\(O(1)\)):

void del(LL c)
{
    LL o = c - 1;
    s = ((((s & (~ c)) & (~ o)) >> 1) ^ (s & o));
}
void insert(LL c)
{
    LL o = c - 1;
    x = (((x & (~ o)) << 1) ^ (x & o));
} 

然后还有一个问题就是,我们首先要找到满足\([a,min_{b,p-1}]\)的最大值(知道了以后就可以解题了)。

我们同样先不考虑后面的限制,我们把\(min_{b,p-1}\)\(p\)列出来从高位到低位枚举,假设蒟蒻的站队方式最大值为\(r\),如果我们枚举到一位,满足这一位\(p\)是1,\(min_{b,p-1}\)也是1,由于约束,则蒟蒻的站队在这一位上必须是0,由于是从高位往低位枚举,则高位上肯定没有这种情况(否则就不会出现在这里了),那\(r\)在这位上肯定是0,这时,可以发现,无论后面取什么数,其结果都比\(min_{b,p-1}\)小!那我们的\(r\)值就可以取到这位是0,后面都是1的情况。

对于其他情况,不难发现,我们必须要取\(min_{b,p-1}\)所在的值才能保证最大。

最后,找到了\(r\)后,我们去除限制条件,把所有巨佬所在值为1的权位上的值改为0,我们就找到了最大值。

代码也不长(甚至连数组都不用开),但是如果没思路的话,代码不一定看得懂:

#include<cstdio>
#include<iostream>
#define LL unsigned long long
using namespace std;
LL n,m,t,q;
LL p,a,b,k,s,x;
void del(LL c)
{
    LL o = c - 1;
    s = ((((s & (~ c)) & (~ o)) >> 1ll) ^ (s & o));
}
void getmax()
{
    LL j = (1ll << (n - 1));
    for( ;j ; (j >>= 1))
      if((b & j) && (p & j)) break;
      if(!j) k = b;
      else k = ((b & (~ j)) | (j - 1));
    j = (1ll << (n - 1));
    for(; j; (j >>= 1))
      if(p & j) k = (k & (~ j));
    j = (1ll << (n - 1));s = k;
    for(; j; (j >>= 1))
      if((k & (~ (j - 1))) && (p & j)) del(j);
}
void insert(LL c)
{
    LL o = c - 1;
    x = (((x & (~ o)) << 1ll) ^ (x & o));
} 
int main()
{
    scanf("%lld%lld", &n,&m);
    for(LL i = 0;i<n;++i)
    {
        scanf("%lld", &t);
        p |= (t << i);
    }
    while(m --)
    {
        scanf("%lld%lld%lld",&a,&b,&q);
        b = min(p-1, b);
        getmax();
        x = s - q + 1;
        for(LL j = 1;j <= (1ll << n);j <<= 1)
          if(p & j) insert(j);
         if(x < a|| x>p) printf("POOR AFO!\n");
         else printf("%lld\n", x % 20031102);
    }
}

CTYZ信心赛T5 题解

标签:一个   cst   while   其他   code   ||   解决   scanf   iostream   

原文地址:https://www.cnblogs.com/dwqhca/p/10185318.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!