[洛谷P4424][HNOI/AHOI2018]寻宝游戏(bitset)

P4424 [HNOI/AHOI2018]寻宝游戏



某大学每年都会有一次Mystery Hunt的活动,玩家需要根据设置的线索解谜,找到宝藏的位置,前一年获胜的队伍可以获得这一年出题的机会。

作为新生的你,对这个活动非常感兴趣。你每天都要从西向东经过教学楼一条很长的走廊,这条走廊是如此的长,以至于它被人戏称为infinite corridor。一次,你经过这条走廊时注意到在走廊的墙壁上隐藏着nn 个等长的二进制的数字,长度均为mm 。你从西向东将这些数字记录了下来,形成一个含有nn 个数的二进制数组a_1,a_2,...,a_na1?,a2?,...,an? 。

很快,在最新的一期的Voo Doo杂志上,你发现了qq 个长度也为mm 的二进制数r_1,r_2,...,r_qr1?,r2?,...,rq? 。

聪明的你很快发现了这些数字的含义。

保持数组a_1,a_2,...,a_na1?,a2?,...,an? 的元素顺序不变,你可以在它们之间插入∧∧ (按位与运算)或者∨∨ (按位或运算)。例如:11011∧00111=0001111011∧00111=00011 ,11011∨00111=1111111011∨00111=11111 。

你需要插入nn 个运算符,相邻两个数之前恰好一个,在第一个数的左边还有一个。如果我们在第一个运算符的左边补入一个0,这就形成了一个运算式,我们可以计算它的值。与往常一样,运算顺序是从左到右。有趣的是,出题人已经告诉你这个值的可能的集合——Voo Doo杂志里的那些二进制数r_1,r_2,...,r_qr1?,r2?,...,rq? ,而解谜的方法,就是对r_1,r_2,...,r_qr1?,r2?,...,rq? 中的每一个值r_iri? ,分别计算出有多少种方法填入这nn 个计算符,使的这个运算式的值是r_iri? 。

然而,infinite corridor真的很长,这意味着数据范围可能非常大。因此,答案也可能非常大,但是你发现由于谜题的特殊性,你只需要求答案模1000000007的值。

输入输出格式



输入格式:

第一行三个数nn ,mm ,qq ,含义如题所述。

接下来nn 行,其中第ii 行有一个长度为mm 的二进制数,左边是最高位,表示a_iai? 。

接下来qq 行,其中第ii 行有一个长度为mm 的二进制数,左边是最高位,表示r_iri? 。

输出格式:

输出qq 行,每行一个数,其中的ii 行表示对于r_iri? 的答案。

输入输出样例


输入样例#1:

5 5 1
01110
11011
10000
01010
00100
00100

输出样例#1:


6

输入样例#2:


10 10 3
0100011011
0110100101
1100010100
0111000110
1100011110
0001110100
0001101110
0110100001
1110001010
0010011101
0110011111
1101001010
0010001001

输出样例#2:


69
0
5

说明



对于 10% 的数据,n \le 20, m \le 30, q = 1n≤20,m≤30,q=1

对于另外 20 的数据,n \le 1000, m \le 16n≤1000,m≤16

对于另外 40 的数据,n \le 500, m \le 1000n≤500,m≤1000

对于全部的数据1≤n≤1000,1≤m≤5000,1≤q≤10001≤n≤1000,1≤m≤5000,1≤q≤1000

分析:



倒着来推,每次枚举每个位置是or 还是 and。设每个串为S[i],设每次查询的串为G

然后有个表 :(设当前枚举到第i个字符串的第j位,设F[i][j]表示(1 ~ i - 1)操作后第j位为0/1)

————————————

F[i - 1][j]  S[i][j]  G[j]   type

0  /  1       0       0      and

0              1       0      and

1              1       1      and

-1             0       1      and

0  /  1       1       1      or

0              0       0      or

1              0       1      or

-1             1      0       or

————————————

-1表示(1~i - 1)的操作怎么枚举都不可能满足,0/1表示(1 ~ i - 1)的操作随便枚举都可以满足。

这样我们倒着2^n次方枚举所有填的可能,当出现-1就剪枝,当全部位都为0/1就可以直接加上2^(i - 1),并剪枝。

这样剪下来,每一层状态数最多为2,是不是很神奇。。

然后复杂度就变成了O(nq * (位运算代价))。

使用bitset(要手写),把位运算代价优化成5000 / 64 = 78

总复杂度O(78nq)

AC代码:


# include <iostream>
# include <cstdio>
# include <cstring>
using namespace std;
typedef unsigned long long llu;
const int N = 1012;
const int mod = 1e9 + 7;
int block,n,m,Q;
struct bitset{
   llu p[80];
   void read()
   {
       memset(p,0llu,sizeof p);
       char i = getchar();
    while(!isdigit(i))i = getchar();
    for(int j = 0;j < m;j++,i = getchar())p[j / 64] |= (((llu)(i - ‘0‘)) << (j % 64));
   }
   void clear(){for(int i = 0;i <= block;i++)p[i] = 0;}
}s[N],f[N],nt,t1,t2,v1,v2,g,c1,c2,que[2][4];
int bac[N],ret,dt,cur,tot,ans;
void init()
{
    block = (m - 1) / 64;
    bac[0] = 1;for(int i = 1;i <= n;i++)bac[i] = (bac[i - 1] << 1) % mod;
    memset(nt.p,0llu,sizeof nt.p);
    for(int j = 0;j < m;j++)nt.p[j / 64] |= (1llu << (j % 64));
    for(int i = 1;i <= n;i++)
    {
        s[i].read();
        for(int j = 0;j <= block;j++)
        f[i].p[j] = s[i].p[j] ^ nt.p[j];
    }
}
int main()
{
  scanf("%d %d %d",&n,&m,&Q);
  init();
  while(Q--)
  {
      g.read();dt = ans = cur = 0;que[cur][++dt].clear();
      for(int i = n;i;i--)
      {
          tot = dt;cur ^= 1;dt = 0;
          for(int j = 1;j <= tot;j++)
          {
            bool f1 = true,f2 = true,l1 = true,l2 = true;
            for(int k = 0;k <= block;k++)
          {
           if(!f1 && !f2)break;
           if(que[cur ^ 1][j].p[k] == nt.p[k])
           {
                if(f1)t1.p[k] = nt.p[k];
                if(f2)t2.p[k] = nt.p[k];
                continue;
           }
           if(f1)v1.p[k] = s[i].p[k] & (s[i].p[k] ^ g.p[k]);
           if(f2)v2.p[k] = g.p[k] & (s[i].p[k] ^ g.p[k]);
           if(f1 && (v1.p[k] & que[cur ^ 1][j].p[k]) != v1.p[k])f1 = false;
           if(f2 && (v2.p[k] & que[cur ^ 1][j].p[k]) != v2.p[k])f2 = false;
           if(f1)t1.p[k] = que[cur ^ 1][j].p[k] | (s[i].p[k] ^ v1.p[k]);
           if(f2)t2.p[k] = que[cur ^ 1][j].p[k] | (f[i].p[k] ^ v2.p[k]);
           if(f1 && t1.p[k] != nt.p[k])l1 = false;
           if(f2 && t2.p[k] != nt.p[k])l2 = false;
          }
          if(f1)
          {
              if(l1)ans = (ans + bac[i - 1]) % mod;
              else que[cur][++dt] = t1;
          }
          if(f2)
          {
              if(l2)ans = (ans + bac[i - 1]) % mod;
              else que[cur][++dt] = t2;
          }
          }
      }
      for(int j = 1;j <= dt;j++)
      {
          bool f1 = true;
          for(int k = 0;k <= block;k++)
          {
              if((que[cur][j].p[k] ^ nt.p[k]) & g.p[k]){f1 = false;break;}
          }
          ans += f1;
      }
      printf("%d\n",ans);
  }
}

原文地址:https://www.cnblogs.com/lzdhydzzh/p/8850109.html

时间: 2024-11-10 21:48:01

[洛谷P4424][HNOI/AHOI2018]寻宝游戏(bitset)的相关文章

洛谷 P1005 矩阵取数游戏

题目描述 帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n*m的矩阵,矩阵中的每个元素aij均为非负整数.游戏规则如下: 1.每次取数时须从每行各取走一个元素,共n个.m次后取完矩阵所有元素: 2.每次取走的各个元素只能是该元素所在行的行首或行尾: 3.每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值*2^i,其中i表示第i次取数(从1开始编号): 4.游戏结束总得分为m次取数得分之和. 帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分. 输入输

洛谷 P1005 矩阵取数游戏 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:https://www.luogu.org/problem/show?pid=1005 题目描述 帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n*m的矩阵,矩阵中的每个元素aij均为非负整数.游戏规则如下: 1.每次取数时须从每行各取走一个元素,共n个.m次后取完矩阵所有元素: 2.每次取走的各个元素只能是该元素所在行的行首或行尾: 3.每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元

dp+高精度(洛谷1005 矩阵取数游戏NOIP 2007 提高第三题)

帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n*m的矩阵,矩阵中的每个元素aij均为非负整数.游戏规则如下: 1.每次取数时须从每行各取走一个元素,共n个.m次后取完矩阵所有元素: 2.每次取走的各个元素只能是该元素所在行的行首或行尾: 3.每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值*2^i,其中i表示第i次取数(从1开始编号): 4.游戏结束总得分为m次取数得分之和. 帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分. 输入格式: 输入

洛谷P2964 [USACO09NOV]硬币的游戏A Coin Game

题目描述 Farmer John's cows like to play coin games so FJ has invented with a new two-player coin game called Xoinc for them. Initially a stack of N (5 <= N <= 2,000) coins sits on the ground; coin i from the top has integer value C_i (1 <= C_i <=

洛谷P1005 矩阵取数游戏

P1005 矩阵取数游戏 题目描述 帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n*m的矩阵,矩阵中的每个元素aij均为非负整数.游戏规则如下: 1.每次取数时须从每行各取走一个元素,共n个.m次后取完矩阵所有元素: 2.每次取走的各个元素只能是该元素所在行的行首或行尾: 3.每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值*2^i,其中i表示第i次取数(从1开始编号): 4.游戏结束总得分为m次取数得分之和. 帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出

洛谷 P1747 好奇怪的游戏

P1747 好奇怪的游戏 题目背景 <爱与愁的故事第三弹·shopping>娱乐章. 调调口味来道水题. 题目描述 爱与愁大神坐在公交车上无聊,于是玩起了手机.一款奇怪的游戏进入了爱与愁大神的眼帘:***(游戏名被打上了马赛克).这个游戏类似象棋,但是只有黑白马各一匹,在点x1,y1和x2,y2上.它们得从点x1,y1和x2,y2走到1,1.这个游戏与普通象棋不同的地方是:马可以走“日”,也可以像象走“田”.现在爱与愁大神想知道两匹马到1,1的最少步数,你能帮他解决这个问题么? 输入输出格式

洛谷P1290 欧几里德的游戏 数学 博弈论 模拟

洛谷P1290 欧几里德的游戏 数学 博弈论 模拟 这道题我们因为当 x 大于 y 时 你也只能在合法范围 内取 1 个 y 两个 y 也就是说 能取的y大于等于2时,则你本质不同的取法共有两种,此时你必定获胜,因为本质不同,而在最优策略下,则说明胜利者也不同,也就是说这时候你可以决定自己的输赢 ,我们称这种必胜局为 v 局 2.但是如果 v 局后面还有v 局怎么办,这个不必担心,因为先拿到 v局的人,有两种本质不同的取法,也就是说 他可以控制自己下次必定拿到 v 局,这样就 能确保胜利了 所以

洛谷P1080 国王游戏 高精度 贪心 数学推公式

洛谷P1080 国王游戏        数学推公式      高精度    贪心 然而这并不是我打出来的,抄题解... 将左手与右手的乘积从小到大排序,然后计算求最大值即可.(需要高精度) 证明: 1)知道,如果相邻的两个人交换位置,只会影响到这两个人的值,不会影响他人 2)假设相邻的两个人i, i + 1.设A[i] B[i] <= A[i + 1] B[i + 1],i之前所有人的左手乘积为S. 则,ans1 = max{S / B[i], S * A[i] / B[i + 1]} 若交换

洛谷P1199 三国游戏 博弈论 数学

洛谷P1199 三国游戏博弈论 数学 这道题 其实 人是必胜的 能取到的最大值 为 每行第二大值中的最大值 为什么呢 假使第一次我们取到了我们心中的那个答案的所在行 那么接着电脑会取 这一行最大值的对应行 那么我们来分析一下电脑取的那个数在他取的那行的特点 首先我们假设其不是最大值,因为矩阵中任意两个数不相同 所以也就是说还有个数比他大,然后就是说电脑取的第二大或者第n大值就已经比我们取的第一大值要大了,说明我们取的行并不是每行第二大值中的最大值所在的行2.也就是说这个数必定为该行中的最大值 那