【北大信息学夏令营模拟2019.5.21】神犇

Description:

【北大信息学夏令营模拟2019.5.21】神犇_第1张图片
【北大信息学夏令营模拟2019.5.21】神犇_第2张图片

时限:2s
空限:256MB

题解:

这题非常容易想到先做个前缀和

(a,b,c)分别表示三个人的粉丝数

不妨变成(a-b,a-c,b-c)

这样相当于要从前面的前缀中选个三维都不一样的,使异或和最大。

那么这个可以暴力容斥地建出八种trie(每一维选和不选),然后自高位到低位的贪心,有的话走过去即可。

注意如果有两维相同第三维也一定相同,所以优化到5种trie
复杂度 O ( n l o g ∣ V ∣ ∗ 5 ) O(nlog|V|*5) O(nlogV5),可惜trie的空间有点大,不行

劳动人民的智慧是无穷的,我们开发trie上动态缩边技巧,反正最后缩出来的点数就是虚树的点数,上限是2n,有一点难打,可以看代码。

题解利用到了几个性质:
1是之前说到的要么一维相同要么三维相同。
2是我们只要判定是否存在而不用求个数。

于是我们先随便记一个(a,b,c)
假设来了(x,y,z)
若完全相等,则记是否存在三维与(a,b,c)不同的即可。

考虑有一维相同,假设就是a=x
记一个(a’,b’,c’)满足a’不等于a

再比较,若此时还有一维相同假设b’=y
再记(a’’,b’’,c’’)满足a’‘≠a,b’‘≠b’
若还不满足,再记(a’’’,b’’’,c’’’)满足a’’‘≠a,b’’‘≠b’,c’’‘≠c’’

其实就是几个二进制状态表示这些数是否存在,似乎有点烦。

我的代码是优化trie的:

#include
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define ff(i, x, y) for(int i = x, B = y; i <  B; i ++)
#define fd(i, x, y) for(int i = x, B = y; i >= B; i --)
#define db double
#define ll long long
#define pp printf
using namespace std;

const int N = 3e5 + 5;

int n, opt;
int a[N], b[N];
int ans[N];

const int M = 3e6;

int son[M][2], tot, siz[M], len[M], t[M];

map<ll, int> rt[5];

const int C = 200005;


ll z2(ll x, ll y) {
	return (x + 1e5) * C + (y + 1e5);
}

int s[N][3];

int f[5] = {1, -1, -1, -1, 2};

int mk;

int W;

int fz(int x) {
	int y = 0;
	fo(i, 0, 29) y += (1 << (29 - i)) * (x >> i & 1);
	return y;
}
void split(int x, int c, int l) {
	int y = son[x][c];
	if(l == len[y]) return;
	len[++ tot] = l;
	t[tot] = t[y] & ((1 << l) - 1);
	siz[tot] = siz[y];
	t[y] >>= l; len[y] -= l;
	son[x][c] = tot; son[tot][t[y] & 1] = y;
}
#define qw(x, y) ((x) >> (y) & 1)
void dd(int &rt) {
	if(!rt) rt = ++ tot;
	int x = rt, T = W, L = 30;
	while(1) {
		int c = T & 1;
		int y = son[x][c], lp = 0;
		while(lp < len[y] && qw(t[y], lp) == qw(T, lp)) lp ++;
		if(!lp) break;
		split(x, c, lp);
		T >>= lp; L -= lp;
		x = son[x][c]; siz[x] ++;
	}
	if(L > 0) {
		int c = T & 1;
		t[++ tot] = T, len[tot] = L;
		son[x][c] = tot;
		siz[tot] ++;
	}
}

void ad(int i) {
	W = fz(W);
	dd(rt[0][0]);
	dd(rt[1][s[i][0]]); dd(rt[2][s[i][1]]); dd(rt[3][s[i][2]]);
	dd(rt[4][z2(s[i][0], s[i][1])]);
}

struct wz {
	int x, l;
} w[5];

int nsiz(wz w, int c) {
	if(w.l == len[w.x]) return siz[son[w.x][c]];
	return qw(t[w.x], w.l) == c ? siz[w.x] : 0;
}
void move(wz &w, int c) {
	if(w.l == len[w.x]) w.l = 1, w.x = son[w.x][c]; else {
		if(qw(t[w.x], w.l) == c) w.l ++; else
		w.x = 0;
	}
}

int main() {
	freopen("imbalance.in", "r", stdin);
	freopen("imbalance.out", "w", stdout);
	
	scanf("%d %d", &n, &opt);

	fo(i, 1, n) scanf("%d", &a[i]);
	fo(i, 1, n) scanf("%d", &b[i]);
	ad(0);
	fo(i, 1, n) {
		if(opt) {
			a[i] = (a[i] ^ ans[i - 1]) % 3;
			b[i] = b[i] ^ ans[i - 1];
		}
		b[i] ^= b[i - 1];
		fo(j, 0, 2) s[i][j] = s[i - 1][j];
		if(a[i] == 0) {
			s[i][0] ++; s[i][1] ++;
		}
		if(a[i] == 1) {
			s[i][0] --; s[i][2] ++;
		}
		if(a[i] == 2) {
			s[i][1] --; s[i][2] --;
		}
		
		w[0].x = rt[0][0];
		w[1].x = rt[1][s[i][0]]; w[2].x = rt[2][s[i][1]]; w[3].x = rt[3][s[i][2]];
		w[4].x = rt[4][z2(s[i][0], s[i][1])];
		fo(j, 0, 4) w[j].l = 0;
		
		fd(j, 29, 0) {
			int c = b[i] >> j & 1;
			int s = 0;
			fo(k, 0, 4) {
				s += f[k] * nsiz(w[k], c ^ 1);
			}
			if(s) {
				ans[i] += 1 << j;
				fo(k, 0, 4) move(w[k], c ^ 1);
			} else {
				fo(k, 0, 4) move(w[k], c);
			}
		}
		W = b[i]; ad(i);
	}
	fo(i, 1, n) pp("%d ", ans[i]);
}

你可能感兴趣的:(Trie)