字典树补题记录

Luogu - P6587 超超的序列 加强

AC on 2023.11.19

发现 \(x \le 20\),可以取编号 01 串的后 \(x\) 位,按字典树的形式,线段树的思想。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define debug(x) cout<<#x<<" = "<<x<<endl;
const int N = 2e5 + 5, T = 4e6 + 5;
int n, m, a[N], point[N*20][2], num, son_num[T];
ll sum[T], tag_add[T], lastans;
inline int read(){
	int s=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){s=(s<<1)+(s<<3)+(c^48);c=getchar();}
	return s*f;
}
void insert(int val, int id){
	int p=0, x=1;
	son_num[x]++;
	sum[x] += val;
	for(int i=0;i<20;i++){
		bool k = id & (1 << i);
		if(!point[p][k])point[p][k] = ++num;
		p = point[p][k];
		if(k)x = x<<1|1;
		else x = x<<1;
		son_num[x]++;
		sum[x] += val;
	}
}
int find(int f, int y){
	int p=0, x=1;
	for(int i=0;i<f;i++){
		bool k = y & (1 << i);
		sum[x<<1] += tag_add[x] * son_num[x<<1];
		sum[x<<1|1] += tag_add[x] * son_num[x<<1|1];
		tag_add[x<<1] += tag_add[x];
		tag_add[x<<1|1] += tag_add[x];
		tag_add[x] = 0;
		if(!point[p][k])return 0; 
		p = point[p][k];
		if(k)x = x<<1|1;
		else x = x<<1;
	}
	return x;
}
void add(int f, int y, ll v){
	int p=0, x=1;
	for(int i=0;i<f;i++){
		bool k = y & (1 << i);
		sum[x<<1] += tag_add[x] * son_num[x<<1];
		sum[x<<1|1] += tag_add[x] * son_num[x<<1|1];
		tag_add[x<<1] += tag_add[x];
		tag_add[x<<1|1] += tag_add[x];
		tag_add[x] = 0;
		if(!point[p][k])return;
		p = point[p][k];
		if(k)x = x<<1|1;
		else x = x<<1;
	}
	tag_add[x] += v;
	int now_son_num = son_num[x];
	while(x){sum[x] += v * now_son_num; x>>=1;}//线段树的 pushup 
}
int main(){
	n = read(), m = read();
	for(int i=1;i<=n;i++)a[i] = read(), insert(a[i], i);
	while(m--){
		int op = read();
		int opt = (lastans + op) % 2 + 1;
		if(opt == 1){
			int x = read(), y = read(), v = read();
			add(x, y, v);
		}else{
			int x = read(), y = read();
			lastans = sum[find(x, y)];
			cout << lastans << endl;
		}
	}
	return 0;
}

\(\,\)

P2922 [USACO08DEC] Secret Message G

感觉模版题啊,建议黄题吧

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define debug(x) cout<<#x<<" = "<<x<<endl;
const int N = 1e4 + 5, T = 5e5 + 5;
int m, n, point[N][2], tail[T], cnt[T], num;
// tail 每个 insert 的末端加 1   cnt 每个 insert 的路径上都加 1 
int s, opt;
inline int read() {
	int s = 0, f = 1; char c = getchar();
	while (c < '0' || c>'9') { if (c == '-')f = -1; c = getchar(); }
	while (c >= '0' && c <= '9') { s = (s << 1) + (s << 3) + (c ^ 48); c = getchar(); }
	return s * f;
}
void insert() {
	s = read();
	int p = 0;
	while (s--) {
		opt = read();
		if (!point[p][opt])point[p][opt] = ++num;
		cnt[p]++;
		p = point[p][opt];
	}
	tail[p]++;
}
void query() {
	s = read();
	int p = 0, ans = 0;
	while (s--) {
		opt = read();
		if (!point[p][opt]) { cout << ans << endl; while (s--)opt = read(); return; }
		p = point[p][opt];
		ans += tail[p];
	}
	cout << ans + cnt[p] << endl;
}
int main() {
	m = read(), n = read();
	while (m--)insert();
	while (n--)query();
	return 0;
}

\(\,\)

CF817E Choosing The Commander

AC on 2023.12.23

只需要异或后每一个二进制位都小于限制的二进制位就行了,分类讨论啊,\(p\) 为当前要异或的值,\(l\) 为当前要满足的限制:

① p = 0 , l = 0
left
② p = 0 , l = 1
ans += sum_left , right
③ p = 1 , l = 0
right
④ p = 1 , l = 1
ans += sum_right , left  
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define debug(x) cout<<#x<<" = "<<x<<endl;
const int N = 1e7;
int m, point[N][2], num, sum[N];
inline int read(){
	int s=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){s=(s<<1)+(s<<3)+(c^48);c=getchar();}
	return s*f;
}
void insert(int val, int d){
	int p = 0;
	for(int i=27;i>=0;i--){
		bool k = val & (1 << i);
		if(!point[p][k])point[p][k] = ++num;
		p = point[p][k];
		sum[p] += d;
	}
}
int find(int val, int lim){
	int p = 0, ans = 0;
	for(int i=27;i>=0;i--){
		bool k = val & (1 << i), l = lim & (1 << i);
		if(l){
			ans += sum[point[p][k]];
			if(!point[p][k^1])return ans;
			p = point[p][k^1];
		}
		else if(!point[p][k])return ans;
		else p = point[p][k];
	}
	return ans;
}
int main(){
	m = read();
	while(m--){
		int opt = read(), p = read();
		if(opt == 1)insert(p, 1);
		if(opt == 2)insert(p, -1);
		if(opt == 3)printf("%d\n", find(p, read()));
	}
	return 0;
}

\(\,\)


CF842D Vitya and Strange Lesson

AC on 2023.12.23

记录一个 \(reversal[i]\) 表示第 i 层反不反转。
然后策略是 : "左子树" 满了就走 "右子树",否则就走 "左子树"。 (这里的 "左子树""右子树" 是看 \(reversal[i]\) 的值的)
记得去重

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define debug(x) cout<<#x<<" = "<<x<<endl;
const int N = 3e5 + 5;
int n, m, a[N], point[N*20][2], num, sum[N*20];
bool rev[25];  //reversal  n.反转 
inline int read(){
	int s=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){s=(s<<1)+(s<<3)+(c^48);c=getchar();}
	return s*f;
}
void insert(int val){
	int p = 0;
	for(int i=20;i>=0;i--){
		bool k = val & (1 << i);
		if(!point[p][k])point[p][k] = ++num;
		p = point[p][k];
		sum[p]++; 
	}
}
int query(int val){
	int p = 0, ans = 0;
	for(int i=20;i>=0;i--){
		rev[i] ^= bool(val & (1 << i));
		if(sum[point[p][rev[i]]] != (1 << i))p = point[p][rev[i]];
		else p = point[p][1 ^ rev[i]], ans |= (1 << i);
	}
	return ans;
}
int main(){
	n = read(), m = read();
	for(int i=1;i<=n;i++)a[i] = read();
	sort(a+1,a+n+1);
	int cnt = unique(a+1,a+n+1) - a - 1;
	for(int i=1;i<=cnt;i++)insert(a[i]);
	while(m--)printf("%d\n", query(read()));
	return 0;
}
posted @ 2023-11-29 20:35  今添  阅读(12)  评论(0)    收藏  举报