Loading

P4108 [HEOI2015]公约数数列 - 分块

题解

可以发现,\(\gcd(a_1,a_2,\dots,a_{k+1})\mid \gcd(a_1,a_2,\dots,a_k)\),因此前缀中不同的 \(\gcd\) 值只有 \(\log\) 种。

考虑分块:每 \(B\) 个元素为一块,每块内维护区间异或和、区间内 \(\gcd\) 的转折点和所有可能的前缀异或值。

查询时,枚举每个块,找到所有的转折点,进行 \(\log\) 次形如“给定 \(l,r,x\),查询最小的 \(p\in [l,r]\) 使得 \(\operatorname{xor}_{1\le k\le p} a_k=x\)”的询问。

若使用哈希表存储可能的异或值,则其修改的时间复杂度为 \(\mathcal{O}(B\log V)\),查询的时间复杂度为 \(\mathcal{O}((B+\frac{n}{B})\log V)\)

代码中采用的是排序后的 vector 代替哈希表,查询会多一个 \(\log\)

代码
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
template<typename T> void Read(T &_x){
	_x=0;int _f=1;
	char ch=getchar();
	while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar();
	while(isdigit(ch)) _x=_x*10+(ch^48),ch=getchar();
	_x*=_f;
}
template<typename T,typename... Args> void Read(T &_x,Args& ...others){
	Read(_x);Read(others...);
}
typedef long long ll;
typedef pair<int,int> pii;
const int N=1e5+5,SQ=505;
#define gcd __gcd
int n,q,a[N];
int bsiz,bcnt,bel[N];
struct Block{
	int l,r,g,v;
	vector<pii> chg,xo;
}bl[SQ];
void Update(int p){
	int xo=0;
	bl[p].g=bl[p].v=0;
	bl[p].chg.clear(),bl[p].xo.clear();
	For(i,bl[p].l,bl[p].r){
		int temp=gcd(bl[p].g,a[i]);
		if(temp!=bl[p].g) bl[p].chg.push_back({i,temp});
		bl[p].g=temp,bl[p].v^=a[i];
		xo^=a[i],bl[p].xo.push_back({xo,i});
	}
	sort(bl[p].xo.begin(),bl[p].xo.end());
}
void Init(){
	bsiz=min(n,300);
	for(int i=1;i<=n;i+=bsiz){
		bl[++bcnt].l=i,bl[bcnt].r=min(n,i+bsiz-1);
	}
	For(i,1,bcnt){
		For(j,bl[i].l,bl[i].r) bel[j]=i;
		Update(i);
	}
}
void Modify(int pos,int x){
	a[pos]=x;
	Update(bel[pos]);
}
pair<int,ll> Bf(ll x,int l,int r){
	pair<int,ll> res{-1,0LL};
	For(i,l,r){
		res.second^=a[i];
		if(res.first==-1&&res.second==x) res.first=i;
	}
	return res;
}
ll QXor(int l,int r){
	if(l>r) return 0;
	if(bel[l]==bel[r]) return Bf(-1,l,r).second;
	ll res=Bf(-1,l,bl[bel[l]].r).second;
	For(p,bel[l]+1,bel[r]-1) res^=bl[p].v;
	res^=Bf(-1,bl[bel[r]].l,r).second;
	return res;
}
int Find(ll x,int l,int r){
	if(bel[l]==bel[r]) return Bf(x,l,r).first;
	auto res1=Bf(x,l,bl[bel[l]].r);
	if(res1.first!=-1) return res1.first;
	x^=res1.second;
	For(p,bel[l]+1,bel[r]-1){
		auto it=lower_bound(bl[p].xo.begin(),bl[p].xo.end(),pii{x,-1});
		if(it!=bl[p].xo.end()&&it->first==x){
			return it->second;
		}
		x^=bl[p].v;
	}
	auto res2=Bf(x,bl[bel[r]].l,r);
	return res2.first;
}
int Solve(ll x){
	int g=0;vector<pii> chg;
	For(p,1,bcnt){
		if(bl[p].chg.empty()) continue;
		auto fr=bl[p].chg.front();
		if(p==1||gcd(g,fr.second)!=g) chg.push_back({fr.first,gcd(g,fr.second)});
		for(auto it=next(bl[p].chg.begin());it!=bl[p].chg.end();++it){
			if(gcd(it->second,g)==gcd(prev(it)->second,g)) continue;
			chg.push_back({it->first,gcd(it->second,g)});
		}
		g=gcd(g,bl[p].g);
	}
	chg.push_back({n+1,0});
	for(auto it=chg.begin();it!=prev(chg.end());++it){
		if(x%it->second) continue;
		ll goal=x/it->second;
		goal^=QXor(1,it->first-1);
		int res=Find(goal,it->first,next(it)->first-1);
		if(res!=-1) return res;
	}
	return -1;
}
int main(){
	Read(n);
	For(i,1,n) Read(a[i]);
	Init();
	Read(q);
	char temp[100];ll x;
	while(q--){
		scanf("%s",temp);Read(x);
		if(temp[0]=='M'){
			int k;Read(k);
			Modify(x+1,k);
		}else{
			int ans=Solve(x);
			if(ans!=-1) printf("%d\n",ans-1);
			else puts("no");
		}
	}
	return 0;
}
posted @ 2021-10-02 20:15  Alan_Zhao_2007  阅读(42)  评论(0编辑  收藏  举报