树状数组

树状数组

之前的 blog,这个足够简短
二维的,这个足够炸裂


搞点新东西。

树状数组维护最值

依旧维护 \((x-\operatorname{lowbit}(x),x]\) 的最值:

  • 更新时每个包含此节点的都得更新一遍,每个包含此节点的块包含 \(\log n\) 个小块,共有 \(\log n\) 个包含此节点的块,\(O(\log^2n)\)
  • 查询时满块直接查,不满一块直接暴力与当前位置求最值。
int a[],t[];// a 表示原数组,t 是树状数组
void add(int pos,int chg){// 实则是修改
	a[pos]=chg;
	while(pos<=n){
		t[pos]=chg;
		for(int i=0;i<(pos&-pos);i++){
			t[pos]=func(t[pos],t[pos-i])// 其中 func 是最值函数 max/min
		}
	}
}
void query(int l,int r){
	int ans=a[r];
	while(r>=l){
		if(r-(r&-r)+1>=l){// 包含
			ans=max(ans,t[r]);
			r-=(r&-r);
		}else{
			ans=max(ans,a[r]);
			--r;
		}
	}
}

哎是不是可重复贡献问题都可以这么做啊。。

例题:[USACO07JAN] Balanced Lineup G

y1s1,这题是不是没有修改。。

树状数组解决区间颜色数问题

这题我们发现全异或起来是奇数次的整数异或和,所以我们可以将区间去重取异或和再异或起来。

\(lst_x\) 表示 \(x\) 在数组 \(a\)当前下标 \(\boldsymbol{i}\) 最后一次出现的位置。因为如果区间 \([j,i)\) 包含 \(x\),一定会包含 \(x\) 最后一次出现的位置。那么对于 \(a_i\),如果 \(lst_{a_i}\neq 0\),那么 \(\operatorname{add}(lst_{a_i},a_i)\) 即撤回(就本题树状数组维护的是异或和),再 \(\operatorname{add}((lst_{a_i}\gets i),a_i)\)。最后维护一个指针,如果指针指向的当前询问是类似 \([j,i]\)\(\boldsymbol{i}\) 是当前下标!) 的询问,答案就是

\[\operatorname{query}(i,j)\oplus\displaystyle\bigoplus_{k=j}^ia_k \]

后者用前缀和维护。

#define pbds
// #define many
#ifndef pbds
#include<bits/stdc++.h>
using namespace std;
using namespace __gnu_cxx;
#define pqueue priority_queue
#else
#include<bits/extc++.h>
using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
#define unordered_map __gnu_pbds::gp_hash_table
#define pqueue __gnu_pbds::priority_queue
typedef trie<string,null_type,trie_string_access_traits<>,pat_trie_tag,trie_prefix_search_node_update>Trie;
#endif
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int>pii;
typedef pair<ll,ll>pll;
typedef pair<ull,ull>pull;
template<size_t _Nw>
using arri=array<int,_Nw>;
template<size_t _Nw>
using arrl=array<ll,_Nw>;
template<typename Tp>
using heap=pqueue<Tp,greater<Tp>>;
mt19937 mt(chrono::system_clock::now().time_since_epoch().count());
mt19937_64 mt64(chrono::system_clock::now().time_since_epoch().count());
const bool mdsw=true;
const int mod=mdsw?998244353:(int)(1e9+7);
template<typename T=ll>
class fnw{
	vector<T>t;
public:
	fnw(size_t n=5e5){t.resize(n+5);}
	void add(int pos,T v){
		for(int i=pos;i<t.size();i+=(i&-i))t[i]^=v;
	}
	T query(int pos){
		T ans=0;
		for(int i=pos;i>0;i-=(i&-i))ans^=t[i];
		return ans;
	}
};
void solve(){
	int n,q;
	cin>>n;
	vector<int>arr(n+1),s(n+1);
	for(int i=1;i<=n;i++)cin>>arr[i],s[i]=s[i-1]^arr[i];
	unordered_map<int,int>lst;
	cin>>q;
	vector<array<int,4>>Q(q);
	for(int i=0;i<q;i++)cin>>Q[i][0]>>Q[i][1],Q[i][2]=i;
	sort(Q.begin(),Q.end(),[](auto x,auto y){return x[1]<y[1];});
	int now=0;
	fnw t(n);
	for(int i=1;i<=n&&now<q;i++){
		if(lst[arr[i]])t.add(lst[arr[i]],arr[i]);
		lst[arr[i]]=i;
		t.add(lst[arr[i]],arr[i]);
		while(now<q&&Q[now][1]==i)
			Q[now][3]=(t.query(i)^t.query(Q[now][0]-1)^s[i]^s[Q[now][0]-1]),++now;
	}
	sort(Q.begin(),Q.end(),[](auto x,auto y){return x[2]<y[2];});
	for(auto[l,r,id,ans]:Q)cout<<ans<<"\n";
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr),cout.tie(nullptr);
	int oT_To;
	for(
#ifdef many
	cin>>oT_To
#else
	oT_To=1
#endif
	;oT_To--;solve());
	long double some_Random_in_double=uniform_real_distribution<long double>(mt64(),mt64())(mt64);
	if(some_Random_in_double==NAN||some_Random_in_double==INFINITY||abs(some_Random_in_double-some_Random_in_double)>114514)return 114514;
	else if(1+1==3)exit(0x3f3f3f3f);
	else if(clock()==66666)return clock();
	else if(!time(nullptr))return 1919810;
	else return 0;
}

再看这道题

这个有点像,不过左端点、右端点严格递增,我就赛时想到了双指针加 kth 函数。

另:为什么这道题我 kth 不开 O2 见祖宗了?!

例题:HH 的项链BaoBao Loves Reading

我绝不会说前者我用莫队、后者我用主席树过的

HH 的项链:

#define pbds
// #define many
#ifndef pbds
#include<bits/stdc++.h>
using namespace std;
using namespace __gnu_cxx;
#define pqueue priority_queue
#else
#include<bits/extc++.h>
using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
#define unordered_map __gnu_pbds::gp_hash_table
#define pqueue __gnu_pbds::priority_queue
typedef trie<string,null_type,trie_string_access_traits<>,pat_trie_tag,trie_prefix_search_node_update>Trie;
#endif
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int>pii;
typedef pair<ll,ll>pll;
typedef pair<ull,ull>pull;
template<size_t _Nw>
using arri=array<int,_Nw>;
template<size_t _Nw>
using arrl=array<ll,_Nw>;
template<typename Tp>
using heap=pqueue<Tp,greater<Tp>>;
mt19937_64 mt64(chrono::system_clock::now().time_since_epoch().count());
namespace mo_algo{
#ifndef ONLINE_JUDGE
#define gc _getchar_nolock
#define pc _putchar_nolock
#else
#define gc getchar_unlocked
#define pc putchar_unlocked
#endif
	template<typename read_type>
	inline void read(read_type&t){
		t=0;
		int f=1;
	#define nw c=gc()
		char nw;
		while(!isdigit(c)){if(c=='-')f=-1;nw;}
		while(isdigit(c))t=(t<<3)+(t<<1)+c-48,nw;
	#undef nw
		if(!~f)t=-t;
	}
	template<typename read_type>
	inline read_type read(){
		read_type t=0;
		int f=1;
	#define nw c=gc()
		char nw;
		while(!isdigit(c)){if(c=='-')f=-1;nw;}
		while(isdigit(c))t=(t<<3)+(t<<1)+c-48,nw;
	#undef nw
		if(!~f)t=-t;
		return t;
	}
	template<typename rdtype,typename...read_type>
	inline void read(rdtype&rd,read_type&...args){
		read(rd);
		read(args...);
	}
	template<typename write_type>
	inline void write(write_type t){
		static stack<char,vector<char>>sta;
		do sta.push(48|t%10);while(t/=10);
		while(!sta.empty())pc(sta.top()),sta.pop();
	}
	template<typename wttype,typename...write_type>
	inline void write(wttype wt,write_type...args){
		write(wt);
		pc(' ');
		write(args...);
	}
	static ll blk;
	static int n,q;
	struct node{
		int l,r,id;
		bool operator<(const node&nd)const{
			if(l/blk!=nd.l/blk)return l<nd.l;
			return((l/blk)&1?r>nd.r:r<nd.r);
		}
	};
	static int cnt=0;
	static const ll maxn=1e6+5;
	static int occ[maxn],vec[maxn];
	static node que[maxn];
	static int ans[maxn],from[maxn],ttt;
	inline void solve(){
		read(n);
		blk=max(sqrt(n),1.0);
		for(register int i=0;i<n;++i)read(vec[i]),from[vec[i]]?vec[i]=from[vec[i]]:vec[i]=from[vec[i]]=++ttt;
		read(q);
		for(register int i=0;i<q;++i)read(que[i].l,que[i].r),--que[i].l,--que[i].r,que[i].id=i;
		sort(que,que+q);
		register int L=0,R=-1;
		for(register int i=0;i<q;++i){
			register int l=que[i].l,r=que[i].r,id=que[i].id;
			while(L>l)--L,cnt+=(occ[vec[L]]==0),++occ[vec[L]];
			while(R<r)++R,cnt+=(occ[vec[R]]==0),++occ[vec[R]];
			while(L<l)cnt-=(occ[vec[L]]==1),--occ[vec[L]],++L;
			while(R>r)cnt-=(occ[vec[R]]==1),--occ[vec[R]],--R;
			ans[id]=cnt;
		}
		for(register int i=0;i<q;++i)write(ans[i]),pc('\n');
	}
}
namespace fenwick{
	template<typename T=ll>
	class fnw{
		vector<T>t;
	public:
		fnw(size_t n=5e5){t.resize(n+5);}
		void add(int pos,T v){
			for(int i=pos;i<t.size();i+=(i&-i))t[i]+=v;
		}
		T query(int pos){
			T ans=0;
			for(int i=pos;i>0;i-=(i&-i))ans+=t[i];
			return ans;
		}
	};
	void solve(){
		ios::sync_with_stdio(false);
		cin.tie(nullptr),cout.tie(nullptr);
		int n,q;
		cin>>n;
		vector<int>arr(n+1);
		for(int i=1;i<=n;i++)cin>>arr[i];
		unordered_map<int,int>lst;
		cin>>q;
		vector<array<int,4>>Q(q);
		for(int i=0;i<q;i++)cin>>Q[i][0]>>Q[i][1],Q[i][2]=i;
		sort(Q.begin(),Q.end(),[](auto x,auto y){return x[1]<y[1];});
		int now=0;
		fnw<>t(n);
		for(int i=1;i<=n&&now<q;i++){
			if(lst[arr[i]])t.add(lst[arr[i]],-1);
			lst[arr[i]]=i;
			t.add(lst[arr[i]],1);
			while(now<q&&Q[now][1]==i)
				Q[now][3]=(t.query(i)-t.query(Q[now][0]-1)),++now;
		}
		sort(Q.begin(),Q.end(),[](auto x,auto y){return x[2]<y[2];});
		for(auto[l,r,id,ans]:Q)cout<<ans<<"\n";
	}
}
int main(){
	int oT_To;
	for(
#ifdef many
	cin>>oT_To
#else
	oT_To=1
#endif
	;oT_To--;(mt64()&1?fenwick::solve():mo_algo::solve()));
	return 0;
}

时间长的就是莫队,时间短的就是 fnw

posted on 2026-01-13 16:59  HydrogenOxygen  阅读(8)  评论(0)    收藏  举报