[BZOJ5285][HNOI2018]寻宝游戏

先贴一段出题人的话:

当时出题的时候很紧张,是真的紧张,因为觉得实在是太水了。

...

我当时很怕结果出来十几个AC,全场70。然后就再也没人找我出题了。

当时就不停的想HNOI有什么水题,是不是比我这个更水来安慰自己。

首先考虑DP,发现如果按照每位依次DP的话不满足无后效性,所以我们从后往前做。

相当于BFS,列出一个真值表,把8种情况都考虑进去即可。

这样我们就可以得到。。10分了。

https://www.cnblogs.com/lzdhydzzh/p/8850109.html

发现如果全都不强制或者有一位存在不合法情况,都可以直接判掉并剪枝,这样每层最多状态数最多为2。

手写bitset压64位跑一下可以过。$O(frac{nmq}{64})$

有点麻烦,看注释吧。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<iostream>
 5 #define rep(i,l,r) for (int i=l; i<=r; i++)
 6 using namespace std;
 7 
 8 typedef unsigned long long ull;
 9 const int N=1010,mod=1e9+7;
10 int bl,n,m,Q,res,dt,cur,tot;
11 ull ans,mi[N];
12 
13 struct bitset{
14     ull p[80];
15     void clear(){ rep(i,0,bl) p[i]=0; }
16     void read(){
17         memset(p,0,sizeof(p)); char ch=getchar();
18         while (!isdigit(ch)) ch=getchar();
19         for (int i=0; i<m; i++,ch=getchar()) p[i>>6]|=(((ull)(ch-'0'))<<(i&63));//压64位
20     }
21 }s[N],f[N],nt,t1,t2,v1,v2,g,c1,c2,que[2][4];
22 
23 void init(){
24     bl=(m-1)>>6; mi[0]=1;
25     rep(i,1,n) mi[i]=(mi[i-1]<<1)%mod;
26     memset(nt.p,0,sizeof(nt.p));
27     for (int i=0; i<m; i++) nt.p[i>>6]|=(1llu<<(i&63));
28     rep(i,1,n){
29         s[i].read();
30         rep(j,0,bl) f[i].p[j]=s[i].p[j]^nt.p[j];
31     }
32 }
33 
34 int main(){
35     freopen("bzoj5285.in","r",stdin);
36     freopen("bzoj5285.out","w",stdout);
37     scanf("%d%d%d",&n,&m,&Q); init();
38     while (Q--){
39         g.read(); dt=ans=cur=0; que[cur][++dt].clear();//0表示必须和询问串一样,1表示不强制
40         for (int i=n; i; i--){
41             tot=dt; cur^=1; dt=0;
42             rep(j,1,tot){
43                 bool f1=1,f2=1,l1=1,l2=1;
44                 rep(k,0,bl){
45                     if (!f1 && !f2) break;
46                     if (que[cur^1][j].p[k]==nt.p[k]){//如果都不强制的话就不需要具体考虑了
47                         if (f1) t1.p[k]=nt.p[k];
48                         if (f2) t2.p[k]=nt.p[k];
49                         continue;
50                     }
51                     //v1为或,v2为且
52                     if (f1) v1.p[k]=s[i].p[k]&(s[i].p[k]^g.p[k]);//询问串这一位为0,当前串这一位为1
53                     if (f2) v2.p[k]=g.p[k]&(s[i].p[k]^g.p[k]);//询问串这一位为1,当前串这一位为0
54                     if (f1 && (v1.p[k]&que[cur^1][j].p[k])!=v1.p[k]) f1=0;//判掉-1
55                     if (f2 && (v2.p[k]&que[cur^1][j].p[k])!=v2.p[k]) f2=0;//判掉-1
56                     if (f1) t1.p[k]=que[cur^1][j].p[k]|(s[i].p[k]^v1.p[k]);//生成新的要求,如果本来就不强制则必定不强制,具体根据真值表
57                     if (f2) t2.p[k]=que[cur^1][j].p[k]|(f[i].p[k]^v2.p[k]);//生成新的要求,如果本来就不强制则必定不强制,具体根据真值表
58                     if (f1 && t1.p[k]!=nt.p[k]) l1=0;//判断是否全部不强制
59                     if (f2 && t2.p[k]!=nt.p[k]) l2=0;//判断是否全部不强制
60                 }
61                 if (f1){ if (l1) ans=(ans+mi[i-1])%mod; else que[cur][++dt]=t1; }
62                 if (f2){ if (l2) ans=(ans+mi[i-1])%mod; else que[cur][++dt]=t2; }
63             }
64         }
65         rep(j,1,dt){
66             bool f1=1;
67             rep(k,0,bl) if ((que[cur][j].p[k]^nt.p[k])&g.p[k]){ f1=0; break; }//最后对 与 判断一遍是否合法
68             ans+=f1;
69         }
70         printf("%llu
",ans);
71     }
72     return 0;
73 }

上面好像是出题人并不喜欢的暴力解法,很不优美,我们把它扔进垃圾桶。

正解是把或看成1,与看成0,通过总结规律发现题目可以变成比大小问题。

https://kelin.blog.luogu.org/solution-p4424

非常神的想法,代码也很简单,将操作数排序然后看哪个区间是合法的即可。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define rg register int
 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 5 using namespace std;
 6 
 7 const int N=5010,mod=1000000007;
 8 int n,m,q,c[2],a[N],b[N],s[N],t[N],mi[N];
 9 char p[N];
10 
11 int up(int x,int y){ x+=y; return (x>=mod)?x-mod:x; }
12 int dn(int x,int y){ x-=y; return (x<0)?x+mod:x; }
13 
14 int main(){
15     freopen("bzoj5285.in","r",stdin);
16     freopen("bzoj5285.out","w",stdout);
17     scanf("%d%d%d",&n,&m,&q);
18     mi[1]=1; rep(i,2,n+1) mi[i]=(mi[i-1]<<1)%mod;
19     rep(i,1,m) a[i]=i;
20     rep(i,1,n){
21         scanf("%s",p+1); c[0]=0; c[1]=m;
22         rep(j,1,m) if (p[j]=='1') s[j]=up(s[j],mi[i]); else c[0]++;
23         for (int j=m; j; j--) b[c[p[a[j]]-48]--]=a[j];
24         swap(a,b);
25     }
26     rep(i,1,m) t[i]=s[a[i]]; t[m+1]=mi[n+1];
27     while (q--){
28         scanf("%s",p+1); int x=m+1,y=0;
29         for (int i=m; i; i--) if (p[a[i]]=='0') { y=i; break; }
30         rep(i,1,m) if (p[a[i]]=='1') { x=i; break; }
31         printf("%d
",(x<y)?0:dn(t[x],t[y]));
32     }
33     return 0;
34 }
原文地址:https://www.cnblogs.com/HocRiser/p/8954335.html