[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 }
题目链接: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。