字典树补题记录
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;
}

浙公网安备 33010602011771号