[Noi2016十连测第五场]二进制的世界

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <cmath>
 5 #include <algorithm>
 6 using namespace std;
 7 #define maxn 100005
 8 #define maxk 256
 9 int n,type,ans,sum,a[maxn],f[maxk][maxk],g[maxk][maxk];
10 bool v[maxk];
11 char st[5];
12 void read(int &x){
13     x=0; int f=1; char ch;
14     for (ch=getchar();!isdigit(ch);ch=getchar()) if (ch=='-') f=-1;
15     for (;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; x*=f;
16 }
17 int hs(int x,int y){
18     if (st[1]=='o') return x|y;
19     if (st[1]=='a') return x&y;
20     if (st[1]=='x') return x^y;
21 }
22 int main(){
23     read(n),scanf("%s",st+1),read(type);
24     for (int i=1;i<=n;i++) read(a[i]);
25     for (int i=1;i<=n;i++){
26         int k=a[i]&255;
27         if (i>1){
28             sum=ans=0;
29             for (int j=0;j<256;j++){
30                 if (g[j][k]==0) continue;
31                 if ((f[j][k]|(hs(j,a[i]>>8)<<8))==ans) sum+=g[j][k];
32                 if ((f[j][k]|(hs(j,a[i]>>8)<<8))>ans) ans=f[j][k]|(hs(j,a[i]>>8)<<8),sum=g[j][k];
33             }
34             if (type==1) printf("%d %d\n",ans,sum);
35             else printf("%d\n",ans);
36         }
37         for (int j=0;j<256;j++){
38             if (f[a[i]>>8][j]==hs(j,k)) g[a[i]>>8][j]++;
39             if (f[a[i]>>8][j]<hs(j,k)) f[a[i]>>8][j]=hs(k,j),g[a[i]>>8][j]=1;
40         }
41     }
42     return 0;
43 }
View Code

题目链接:http://begin.lydsy.com/JudgeOnline/problem.php?id=3014。

题目大意:给定一种运算,为or,ans,xor中的一种,以及一个长度为n的序列,第i个数为ai,我们设第i个人与第j个人的友好度为ai与aj位运算,这种位运算是题目给定的,求第2个人到第n个人与其左边的人友好程度的最大值及达到该最大值的方案数。n<=100000;ai<=2^16;

做法:如果考虑暴力做,因为ai小于等于2^16,所以暴力做可以做到加入O1,而查询O(2^16),显然是不可以的,然而O(2^8*n)是可以过此题的,根据莫队算法的思想,我可以平衡这种暴力,使得两种操作都达到根号n的复杂度。

正解:我们考虑DP,设f[i][j]表示前8位为i,后8位为j位运算后使得后8位的最大值及方案数,怎么做呢,我们在加入一个数时,我们枚举j,设该数前8位为a,后8位为b,我们用j~b(~表示位运算)来更新f[a][j],查询时,设该数前8位为a,后8位为b,我们可以枚举i用f[i][b]|((i~a)<<8)更新答案即可,复杂度为O(2^8*n)。

dp。

posted @ 2016-06-22 15:59  oyzx~  阅读(326)  评论(0编辑  收藏  举报