[JZOJ6057]【GDOI2019模拟2019.3.13】小凯的疑惑

Description

有一张n个点的完全图,每个点有点权xi,边(i,j)的边权为xi xor xj
有q组询问,每次询问给出v,问将所有点权加上v对2^c-1取模之后这张图的最小生成树
n,q<=20000,c<=14

Solution

EricHuang出的神仙题 Orz
首先n和q的范围都只有2^c-1,复杂度只和c有关
然后这个暴力相信大家都会,是经典的异或生成树,是建trie然后分治
考虑折半,将询问变为[0,B),[B,C)这两段,如果我们预处理出所有 2 B 2^B 2B种前半段的询问中, 所有深度为 2 C − B 2^{C-B} 2CB的子树内部的答案以及两两匹配的结果,我们就可以用 2 C − B 2^{C-B} 2CB的复杂度做一次询问,询问的复杂度是 O ( 2 2 C − B C 2 ) O(2^{2C-B}C^2) O(22CBC2)
考虑这个预处理怎么做,可以再折半,考虑变为[0,A),[A,B)这两段,注意到A不会很大,最下面A层总共可能有 2 2 A 2^{2^A} 22A种不同的子树,我们可以对于每一种子树都暴力求答案并且两两匹配的结果,这里是 O ( 2 2 A + 1 2 2 A + 2 2 A 2 A A 2 ) O(2^{2^{A+1}}2^{2A}+2^{2^A}2^{A}A^2) O(22A+122A+22A2AA2)
接下来我们枚举每个 2 B 2^B 2B的询问和每个 2 C − B 2^{C-B} 2CB的子树,然后往上合并,这里是 O ( 2 2 C − B ( B − A ) 2 ) O(2^{2C-B}(B-A)^2) O(22CB(BA)2)
然后枚举两个深度为 2 C − B 2^{C-B} 2CB的子树匹配,这里是 O ( 2 2 C − A ( B − A ) ) O(2^{2C-A}(B-A)) O(22CA(BA))
然后就做完了,注意到C很小我们可以直接取B=C/A,A=B/2

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define mp(a,b) make_pair(a,b)
using namespace std;

typedef pair<int,int> pr;

const int N=(1<<15)+5,M=(1<<8)+5;

int n,A,B,C,x,q,a[N],sum[N],mask[N],m,v[M];

struct Trie{
	int son[N<<4][2],val[N<<4],tot;
	void clear() {
		fo(i,1,tot) son[i][0]=son[i][1]=val[i]=0;
		tot=1;
	}
	void insert(int v,int l,int r,int id) {
		int x=1;
		fd(i,r-1,l) {
			int y=v>>i&1;
			if (!son[x][y]) son[x][y]=++tot;
			x=son[x][y];
		}
		val[x]=id;
	}
	pr query(int v,int l,int r) {
		int x=1,tmp=0;
		fd(i,r-1,l) {
			int y=v>>i&1;
			if (son[x][y]) x=son[x][y];
			else x=son[x][1-y],tmp+=1<<i;
		}
		return mp(tmp,val[x]);
	}
}tr;

int h[M],H[M][M];

bool empty(int l,int r) {return l?!(sum[r]-sum[l-1]):!sum[r];}

int solveh(int l,int r) {
	if (l==r) return 0;
	int mid=l+r>>1,ret=m,in=0;
	fo(i,l,mid) in|=v[i];if (!in) return solveh(mid+1,r);
	in=0;fo(i,mid+1,r) in|=v[i];if (!in) return solveh(l,mid);
	tr.clear();
	fo(i,l,mid) if (v[i]) tr.insert(i,0,A,i);
	fo(i,mid+1,r) if (v[i]) ret=min(ret,tr.query(i,0,A).first);
	return ret+solveh(l,mid)+solveh(mid+1,r);
}

void solve_h() {
	fo(i,0,(1<<(1<<A))-1) {
		fo(j,0,(1<<A)-1) v[j]=i>>j&1;
		h[i]=solveh(0,(1<<A)-1);
	}
	fo(i,1,(1<<(1<<A))-1) 
		fo(j,1,(1<<(1<<A))-1) {
			H[i][j]=m;
			fo(k,0,(1<<A)-1) fo(l,0,(1<<A)-1)
				if ((i>>k&1)&&(j>>l&1))
					H[i][j]=min(H[i][j],k^l);
		}
}

int solveg(int s,int l,int r) {
	if (l==r) return h[mask[s+(l<<A)]];
	int mid=l+r>>1,ret=m,in=0;
	fo(i,l,mid) in|=!empty(s+(i<<A),s+(i<<A)+(1<<A)-1);
	if (!in) return solveg(s,mid+1,r);
	in=0;fo(i,mid+1,r) in|=!empty(s+(i<<A),s+(i<<A)+(1<<A)-1);
	if (!in) return solveg(s,l,mid);
	tr.clear();
	fo(i,l,mid) if (!empty(s+(i<<A),s+(i<<A)+(1<<A)-1))
		tr.insert(i<<A,A,B,s+(i<<A));
	fo(i,mid+1,r) if (!empty(s+(i<<A),s+(i<<A)+(1<<A)-1)) {
		pr tmp=tr.query(i<<A,A,B);
		ret=min(ret,tmp.first+H[mask[tmp.second]][mask[s+(i<<A)]]);
	}
	return ret+solveg(s,l,mid)+solveg(s,mid+1,r);
}

int g[M][M],G[M][M][M];

void solve_g() {
	fo(i,0,(1<<B)-1) 
		fo(j,0,(1<<C-B)-1) 
			g[i][j]=solveg(i,j<<B-A,(j<<B-A)+(1<<B-A)-1);
	fo(i,0,(1<<B)-1) 
		fo(j,0,(1<<C-B)-1) {
			if (empty(i+(j<<B),i+(j<<B)+(1<<B)-1)) continue;
			tr.clear();
			fo(k,0,(1<<B-A)-1) 
				if (!empty(i+(j<<B)+(k<<A),i+(j<<B)+(k<<A)+(1<<A)-1)) 
					tr.insert((j<<B)+(k<<A),A,B,i+(j<<B)+(k<<A));
			fo(k,0,(1<<C-B)-1) {
				if (empty(i+(k<<B),i+(k<<B)+(1<<B)-1)) continue;
				if (j==k) continue;
				G[i][j][k]=m;
				fo(l,0,(1<<B-A)-1) 
					if (!empty(i+(k<<B)+(l<<A),i+(k<<B)+(l<<A)+(1<<A)-1)) {
						pr tmp=tr.query((k<<B)+(l<<A),A,B);
						G[i][j][k]=min(G[i][j][k],tmp.first+H[mask[tmp.second]][mask[i+(k<<B)+(l<<A)]]);
					}
			}
		}
}

int solvef(int s1,int s2,int l,int r) {
	int S=(1<<C-B)-1;
	if (l==r) return g[s1][(l+s2)&S];
	int mid=l+r>>1,ret=m,in=0,s=s1+(s2<<B);
	fo(i,l,mid) in|=!empty(s+(i<<B),s+(i<<B)+(1<<B)-1);
	if (!in) return solvef(s1,s2,mid+1,r);
	in=0;fo(i,mid+1,r) in|=!empty(s+(i<<B),s+(i<<B)+(1<<B)-1);
	if (!in) return solvef(s1,s2,l,mid);
	tr.clear();
	fo(i,l,mid)
		if (!empty(s+(i<<B),s+(i<<B)+(1<<B)-1)) 
			tr.insert(i<<B,B,C,i);
	fo(i,mid+1,r)
		if (!empty(s+(i<<B),s+(i<<B)+(1<<B)-1)) {
			pr tmp=tr.query(i<<B,B,C);
			ret=min(ret,tmp.first+G[s1][(i+s2)&S][(tmp.second+s2)&S]);
		}
	return ret+solvef(s1,s2,l,mid)+solvef(s1,s2,mid+1,r);
}

int f[N];

void solve_f() {
	fo(i,0,(1<<B)-1)
		fo(j,0,(1<<C-B)-1)
			f[i+(j<<B)]=solvef(i,j,0,(1<<C-B)-1);
}

void prepare() {
	fo(i,0,m) a[i+(1<<C)]=a[i];
	sum[0]=a[0];fo(i,1,1<<C<<1) sum[i]=sum[i-1]+a[i];
	fo(i,0,m) fo(j,0,(1<<A)-1) mask[i]|=a[i+j]<<j;
	fo(i,0,m) mask[i+(1<<C)]=mask[i];
}

int main() {
	freopen("xor.in","r",stdin);
	freopen("xor.out","w",stdout);
	scanf("%d%d",&n,&C);
	fo(i,1,n) {
		scanf("%d",&x);
		a[x]=1;
	}
	B=C>>1;A=B>>1;m=(1<<C)-1;
	prepare();
	solve_h();solve_g();solve_f();
	int sum=0;
	for(scanf("%d",&q);q;q--) {
		scanf("%d",&x);sum=(sum+x)&m;
		printf("%d\n",f[(m+1-sum)&m]);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值