【平衡规划】JZOJ4616. 【NOI2016模拟7.12】二进制的世界

Desciption

在这里插入图片描述

  • n<=1e5,ai<=216

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("\n");
		ins(k);
	}
}
posted @ 2019-07-07 20:01  Deep_Thinking  阅读(129)  评论(0编辑  收藏  举报