=== ===
=== ===
题解
学姐那次胡策的三道题全用的zyf当题面2333@zyf2000
(Update-两分钟后:zyf2000想要打击报复差点把上面那句话删掉!!!)
一开始还想用字典树搞来着。。但是字典树的话只是处理异或运算比较方便啊。。并且a的范围来讲O(n2)的复杂度也不大科学是吧。
然后就有了一个非常厉害的做法:把数字分成两部分去DP!
用f[i][j]表示出现过的前8位为i的数和某个后8位为j的数做题目中给定的位运算能得到的后8位的最大值,同时用g[i][j]记录方案数。
设当前操作数的前8位是a,后8位是b,插入数的时候枚举所有的j,用j和b的运算结果来更新f[a][j];计算答案的时候枚举所有出现过的i,用i和a的运算结果拼上f[i][b]来更新答案。这样的话因为28非常小啊所以时间复杂度也相当科学。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int Max=256;
int n,type,f[510][510],g[510][510],a[100010];
char opt[10];
bool ext[500];
int calc(int x,int y){
switch (opt[0]){
case 'a':return x&y;
case 'o':return x|y;
case 'x':return x^y;
}
}
void update(int k){
int a,b;
a=k>>8;b=k-(a<<8);
for (int i=0;i<=Max;i++){
int dlt=calc(i,b);
if (dlt>f[a][i]){
f[a][i]=dlt;g[a][i]=1;
}else
if (dlt==f[a][i])
++g[a][i];
}
ext[a]=true;
}
void getans(int k){
int a,b,ans1,ans2;
a=k>>8;b=k-(a<<8);
ans1=ans2=0;
for (int i=0;i<=Max;i++)
if (ext[i]==true){
int dlt=(calc(i,a))<<8;
dlt=dlt|f[i][b];
if (dlt>ans1){ans1=dlt;ans2=g[i][b];}
else if (dlt==ans1) ans2+=g[i][b];
}
if (type==0) printf("%d\n",ans1);
else printf("%d %d\n",ans1,ans2);
}
int main()
{
freopen("binary.in","r",stdin);
freopen("binary.out","w",stdout);
scanf("%d%s%d",&n,&opt,&type);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
update(a[1]);
for (int i=2;i<=n;i++){
getans(a[i]);
update(a[i]);
}
return 0;
}
偏偏在最后出现的补充说明
如果直接把所有数字存在状态里的话65536*65536肯定卡飞对吧但是这样把它分成两部分来搞的话就非常科学了。关键在于利用位运算各位之间的独立性。