Desciption
Solution
- 一道看起来很神仙的题目,实际上运用了平衡规划的神奇技巧。
- 让我们先来看一看暴力。
- 我们设 fi 表示 a=i 时的答案。
- 每一次加入一个数都可以跟任意一个i进行运算更新fi
- 这样我们能做到 O(216) 修改, O(1) 查询。
- 接下来我们引入平衡规划的思想。对于每加入一个数,我们都要分别对其做以上的操作,我们能不能平衡一下,变成 O(28) 修改, O(28) 查询呢?
- 实际上这就是这题的突破口。
- 又因为这是二进制运算,我们不难想到将其拆成两半,一半长度是28,将待查询的数称作A,以加入的数称作B,设 f[s1][s2] 表示A的前半段为s1,B的后半段为s2,A与B前半段运算的最大值。
- 这个状态十分神奇。
- 因为我们可以发现,查询的时候只需要枚举B的后半段,s1 确定,用最大值 f[s1][s2] ,和A的后半段与B的后半段运算的结果相加即可得到答案。 O(28) 枚举。
- 而更新f的时候,s2 确定,枚举s1 更新即可, O(28) 。
- 所以我们就可做到 O(n*28) 的时间复杂度。
- 这是我除了分块以外的第一道平衡规划的题目,十分神奇,这类的题目比较少见,还需要好好掌握。
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define maxs 256
using namespace std;
int n,type,opt,i,j,k,f[maxs][maxs],g[maxs][maxs],fans,gans;
char ch;
int get(int x,int y){
if (opt==1) return x&y;
if (opt==2) return x|y;
if (opt==3) return x^y;
}
void ins(int x){
int y=x&((1<<8)-1);
for(int i=0;i<1<<8;i++) {
int tmp=get(x>>8,i)<<8;
if (!g[i][y]||tmp>=f[i][y]){
if (tmp>f[i][y]) g[i][y]=0;
g[i][y]++,f[i][y]=tmp;
}
}
}
int main(){
scanf("%d ",&n);
ch=getchar();
if (ch=='a') opt=1,ch=getchar(),ch=getchar(); else
if (ch=='o') opt=2,ch=getchar(); else
if (ch=='x') opt=3,ch=getchar(),ch=getchar();
scanf("%d",&type);
scanf("%d",&k); ins(k);
for(i=1;i<n;i++) {
scanf("%d",&k);
gans=fans=0;
for(j=0;j<1<<8;j++) if (g[k>>8][j]){
int tmp=get(j,k&((1<<8)-1))+f[k>>8][j];
if (!gans||tmp>=fans){
if (tmp>fans) gans=0;
gans+=g[k>>8][j],fans=tmp;
}
}
printf("%d ",fans);
if (type) printf("%d",gans);
printf("
");
ins(k);
}
}