P5460 [BJOI2016] IP地址 题解
P5460 [BJOI2016] IP地址 题解
知识点
Trie(字典树),前缀和、差分思想。
题意分析
就是给定 \(n\) 个加入与删除 01 字符串的更新,以及 \(Q\) 个询问:问一个 01 字符串(长度固定为 \(32\))在 \((l,r]\) 这段时间内,最长前缀变化了几次。
思路分析
操作和询问明显都可以先离线下来,然后紧接着处理:我们把询问拆成两个,用前缀和、差分思想,减一下就得到了答案。
那么我们如何更新答案呢?字典树好像可以,但好像又不行。
我们从题目性质入手,在加入一个 01 字符串的时候(规则生效),我们往字典树中加入,影响的只有最后一个点的子树,那我们直接打一个标记,表示整颗子树加了一个 1,但这样对吗?肯定不对啊 (傻子都知道),我们还要考虑比它长的前缀,所以我们在标记下传的时候判断一下后面有没有更长的,没有再下传即可。那么删除(规则失效)同理。
最后查询也非常方便。
时间复杂度 \(O(w(n+q))\),其中 \(w\) 表示 01 字符串的长度,\(w = 32\)。
CODE
#include<bits/stdc++.h>
#define uint unsigned int
#define FOR(i,a,b) for(register int i=(a);i<=(b);++i)
#define DOR(i,a,b) for(register int i=(a);i>=(b);--i)
#define main Main();signed main(){ios::sync_with_stdio(0);cin.tie(0);return Main();}signed Main
using namespace std;
constexpr int N=1e5+10;
int n,Q;
int ans[N];
uint num[N];
vector<int> l[N],r[N];
struct Update{
int opt,len;
uint x;
void Scan(){
string s;
cin>>s,opt=(s[0]=='A'?1:-1),cin>>s,len=s.size();
FOR(i,0,len-1)x+=(s[i]^'0')<<i;
}
}up[N];
class Trie {
private:
#define ls (tr[p][0])
#define rs (tr[p][1])
int tot;
struct node {
int sum,tag,val;
int son[2];
int &operator [](bool i) {
return son[i];
}
} tr[N<<5];
void push_down(int p,int tag) {
tr[p].tag+=tag,tr[p].sum+=tag;
}
void Push_down(int p) {
if(!ls)ls=++tot;
if(!rs)rs=++tot;
if(tr[p].tag) {
if(!tr[ls].val)push_down(ls,tr[p].tag);
if(!tr[rs].val)push_down(rs,tr[p].tag);
tr[p].tag=0;
}
}
void Clear(){
tr[tot=1]={0,0,0,{0,0}};
}
void Update(int p,uint x,int len,int dep,int val) {
return dep>=len?tr[p].val+=val,push_down(p,1):(Push_down(p),Update(tr[p][x&1],x>>1,len,dep+1,val));
}
int Query(int p,uint x,int len,int dep) {
return dep>=len?tr[p].sum:(Push_down(p),Query(tr[p][x&1],x>>1,len,dep+1));
}
#undef ls
#undef rs
public:
void clear(){
Clear();
}
void update(uint x,int len,int val) {
Update(1,x,len,0,val);
}
int query(uint x) {
return Query(1,x,32,0);
}
} tr;
signed main(){
cin>>n>>Q,tr.clear();
FOR(i,1,n)up[i].Scan();
FOR(i,1,Q){
string s;int L,R;cin>>s>>L>>R,l[L].push_back(i),r[R].push_back(i);
FOR(j,0,31)num[i]+=(s[j]^'0')<<j;
}
FOR(i,1,n){
tr.update(up[i].x,up[i].len,up[i].opt);
for(int x:l[i])ans[x]-=tr.query(num[x]);
for(int x:r[i])ans[x]+=tr.query(num[x]);
}
FOR(i,1,Q)cout<<ans[i]<<endl;
return 0;
}
我这里把 01 字符串转成了 unsigned int,这样 我看着舒服 方便一点。

浙公网安备 33010602011771号