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,这样 我看着舒服 方便一点。


posted @ 2024-08-25 15:56  Add_Catalyst  阅读(22)  评论(0)    收藏  举报