zoukankan      html  css  js  c++  java
  • NOI Online 2021 #1 T3 岛屿探险(CDQ分治+Trie)

    NOI Online 2021 #1 T3 岛屿探险

    题目大意

    • n n n座岛屿编号为 1 − n 1-n 1n,每个岛屿有两个值 a i , b i a_i,b_i ai,bi q q q次询问,给出 l , r , c , d l,r,c,d l,r,c,d,求编号在 [ l , r ] [l,r] [l,r]中的岛屿满足 a ⨁ c ≤ min ⁡ ( b , d ) aigoplus cle min(b,d) acmin(b,d)的数量。
    • n , q ≤ 1 0 5 , 1 ≤ a , b , c , d ≤ 2 24 − 1 n,qle10^5,1le a,b,c,dle2^{24}-1 n,q1051a,b,c,d2241

    题解

    • 部分分给了很清晰的提示。
    • 情况一:始终满足 d ≤ b dle b db,则每个岛的 b b b并没有用,把 a a a插入 0 / 1 0/1 0/1的Trie树中,再把询问 c , d c,d c,d带进去查询。这里需要把询问拆成 l − 1 l-1 l1 r r r,然后和编号一起排序离线查询。查询具体的做法是,若 d d d当前位为 0 0 0,则进入异或后为 0 0 0的子树;否则加上异或后为 0 0 0的子树,再进入异或后为 1 1 1的子树。
    • 情况二:始终满足 b ≤ d ble d bd,则每个询问的 d d d没有用,把 a a a b b b一起插入Trie树中,插入的方法和情况一查询的方法类似,把满足条件的子树打上标记。然后再把 c c c带进去查询。同样需要把询问拆成两部分。
    • 满分的做法,可以用CDQ分治,先把询问和插入按 d d d b b b一起排序,然后分成上面两种情况做,每个区间都按编号再排一次序,两个指针边扫边插入和查询即可。
    • 要注意的是查询到Trie树的叶子节点也要加入答案,插入到Trie树的叶子节点也要打上标记。

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define N 100010
    struct node {
    	int o, x, k, c, r;
    }a[N * 3], p[N * 3], q[N * 3], st[N * 3];
    struct {
    	int p[2], s;
    }f[N * 48];
    int ans[N], sum = 1;
    int cmp(node x, node y) {
    	return x.x < y.x;
    }
    void insert(int k, int c) {
    	int x = 1;
    	for(int i = 23; i >= 0; i--) {
    		int o = 0;
    		if(k & (1 << i)) o = 1;
    		if(!f[x].p[o]) f[x].p[o] = ++sum;
    		x = f[x].p[o];
    		f[x].s += c;
    	}
    }
    int find(int k, int mi) {
    	int s = 0, x = 1;
    	for(int i = 23; i >= 0; i--) {
    		int o = 0;
    		if(k & (1 << i)) o = 1;
    		if(mi & (1 << i)) {
    			if(f[x].p[o]) s += f[f[x].p[o]].s;
    			if(f[x].p[!o]) x = f[x].p[!o]; else break;
    		}
    		else {
    			if(f[x].p[o]) x = f[x].p[o]; else break;
    		}
    		if(i == 0) s += f[x].s;
    	}
    	return s;
    }
    void change(int k, int mi, int c) {
    	int x = 1;
    	for(int i = 23; i >= 0; i--) {
    		int o = 0;
    		if(k & (1 << i)) o = 1;
    		if(mi & (1 << i)) {
    			if(!f[x].p[o]) f[x].p[o] = ++sum;
    			f[f[x].p[o]].s += c;
    			if(!f[x].p[!o]) f[x].p[!o] = ++sum;
    			x = f[x].p[!o];
    		}
    		else {
    			if(!f[x].p[o]) f[x].p[o] = ++sum;
    			x = f[x].p[o];
    		}
    	}
    	f[x].s += c;
    }
    int get(int k) {
    	int x = 1, s = 0;
    	for(int i = 23; i >= 0; i--) {
    		int o = 0;
    		if(k & (1 << i)) o = 1;
    		if(f[x].p[o]) x = f[x].p[o]; else break;
    		s += f[x].s;
    	}
    	return s;
    }
    void solve(int l, int r) {
    	if(l == r) return;
    	int mid = (l + r) / 2;
    	solve(l, mid);
    	solve(mid + 1, r);
    	int ps = 0, qs = 0, i;
    	for(i = l; i <= mid; i++) if(a[i].o) p[++ps] = a[i];
    	for(i = mid + 1; i <= r; i++) if(!a[i].o) q[++qs] = a[i];
    	int x = 1;
    	for(i = 1; i <= ps; i++) {
    		while(x <= qs && q[x].r <= p[i].r) insert(q[x].k, 1), x++;
    		ans[p[i].o] += find(p[i].k, p[i].x) * p[i].c;
    	}
    	for(i = 1; i <= qs && q[i].r <= p[ps].r; i++) insert(q[i].k, -1);
    	ps = qs = 0;
    	for(i = l; i <= mid; i++) if(!a[i].o) p[++ps] = a[i];
    	for(i = mid + 1; i <= r; i++) if(a[i].o) q[++qs] = a[i];
    	x = 1;
    	for(i = 1; i <= qs; i++) {
    		while(x <= ps && p[x].r <= q[i].r) change(p[x].k, p[x].x, 1), x++;
    		ans[q[i].o] += get(q[i].k) * q[i].c;
    	}
    	for(i = 1; i <= ps && p[i].r <= q[qs].r; i++) change(p[i].k, p[i].x, -1);
    	int j = l, k = mid + 1, t0 = l - 1;
    	while(j <= mid || k <= r) {
    		if(j <= mid && (k > r || a[j].r <= a[k].r)) st[++t0] = a[j], j++; else st[++t0] = a[k], k++;
    	}
    	for(i = l; i <= r; i++) a[i] = st[i];
    }
    int main() {
    	int n, Q, i, j, tot = 0;
    	scanf("%d%d", &n, &Q);
    	for(i = 1; i <= n; i++) {
    		scanf("%d%d", &a[i].k, &a[i].x);
    		a[++tot].o = 0, a[tot].r = i;
    	}
    	for(i = 1; i <= Q; i++) {
    		int L, R, C, D;
    		scanf("%d%d%d%d", &L, &R, &C, &D);
    		a[++tot].o = i, a[tot].r = R, a[tot].k = C, a[tot].x = D, a[tot].c = 1;
    		a[++tot].o = i, a[tot].r = L - 1, a[tot].k = C, a[tot].x = D, a[tot].c = -1;
    	}
    	sort(a + 1, a + tot + 1, cmp);
    	solve(1, tot);
    	for(i = 1; i <= Q; i++) printf("%d
    ", ans[i]);
    	return 0;
    }
    

    自我小结

    • 询问区间拆成两半,以及位运算计数用Trie树来实现,都是比较常见的套路,但可能由于最近缺乏做题,意识不足,并未在这方面有任何想法。
    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    网络安全从入门到精通 (第五章-3) MSSQL反弹注入
    网络安全从入门到精通 (第五章-2) MySQL注入 — Dns 注入
    网络安全从入门到精通 (第五章-1 )Access注入 — Cookie注入&偏移注入
    网络安全从入门到精通 (第四章-3) 注入之全方位利用-盲注
    网络安全从入门到精通(第四章-2)GET&POST&HEAD注入
    网络安全从入门到精通(第四章-1)SQL注入的原理分析&工具的介绍
    Educational Codeforces Round 85 (Rated for Div. 2)(A,B,C,D)
    1.1整除
    POJ
    Graph HDU
  • 原文地址:https://www.cnblogs.com/LZA119/p/14608425.html
Copyright © 2011-2022 走看看