省选集训 22/23 - 数据结构

[SHOI2006] 作业 Homework

根号分治的板题吧,当 \(x\leq B\) 的时候直接查询。

否则用 set 枚举 \(i\) 查询最小的 \(sum-x\times i\geq 0\)\(sum\) 值取最小即可。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
const int N=300005,B=548;
int n,x,d[N];char op;set<int> st;
void update(int x){
	st.insert(x);
	for(int i=1;i<=B;i++)  d[i]=min(d[i],x%i);
}
int query(int x,int res=INF){
	if(x<=B)  return d[x];
	for(int i=0;;i++){
		auto it=st.lower_bound(x*i);
		if(it==st.end())  return res;
		res=min(res,*it-x*i);
	}
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr),cout.tie(nullptr);
	cin>>n,fill(d,d+N,INF);
	for(int i=1;i<=n;i++){
		cin>>op>>x;
		if(op=='A')  update(x);
		else  cout<<query(x)<<"\n";
	}
}

CF1822G2 Magic Triples

Easy Version 时可以枚举中间数并 \(O(\sqrt{V})\) 枚举因数 \(b\) 计算答案。

Hard Version 中直接枚举中间数的做法当 \(a_i>10^6\) 时会 TLE,考虑基于此优化。

发现 \(\frac{10^9}{10^6}=10^3=\sqrt{10^6}\),所以 \(a_i>10^6\) 时将枚举因数改为枚举倍数即可。

由于值域过大,使用 map 存储信息,注意这题使用 unordered_map 会神秘 TLE。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define N 200005
#define int long long
int t,n,ans,a[N];map<int,int> mp;
int count(int x){return mp.count(x)?mp[x]:0;}
void solve(){
	cin>>n,ans=0,mp.clear();
	for(int i=1;i<=n;i++)  cin>>a[i],mp[a[i]]++;
	for(auto [x,y]:mp){
		ans+=y*(y-1)*(y-2);
		if(x!=1)  ans+=count(1)*count(x*x)*y;
		if(x<=1000000)  for(int i=2;i*i<=x;i++){
			if(x%i!=0)  continue;
			ans+=count(x/i)*count(x*i)*y;
			if(i*i==x)  continue;
			ans+=count(i)*count(x*x/i)*y;
		}
		else  for(int i=2;i*x<=1000000000;i++){
			if(x%i!=0)  continue;
			ans+=count(x/i)*count(x*i)*y;
		}
	}
	return cout<<ans<<"\n",void();
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr),cout.tie(nullptr);
	cin>>t;while(t--)  solve();
}

[Ynoi2013] 无力回天 NOI2017

把并集转交集,考虑对值的出现次数根号分治。

出现次数 \(>B\) 的 bitset 维护,修改 \(O(1)\),查询 \(O(\frac{m}{Bw})\)

出现次数 \(\leq B\) 的哈希表维护集合两两之间答案就行了,修改 \(O(B)\),查询 \(O(1)\)

这里取 \(B=\sqrt{\frac{m}{w}}\) 达到理论最优复杂度 \(O(m\sqrt{\frac{m}{w}})\)

于是我们获得了初版代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
using namespace std;
using namespace __gnu_pbds;
const int N=1000005,B=176;
vector<int> v[N];
bitset<(N+B-1)/B> bs[N];
gp_hash_table<int,int> mp[N];
int n,cnt,sum[N],op[N],x[N],y[N],num[N],app[N];
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr),cout.tie(nullptr);
	cin>>n,fill(num,num+n+1,-1);
	for(int i=1;i<=n;i++){
		cin>>op[i]>>x[i]>>y[i];
		if(op[i]==1)  app[y[i]]++;
		if(op[i]==2&&x[i]>y[i])  swap(x[i],y[i]);
	}
	for(int i=1;i<=n;i++)  if(app[i]>=B)  num[i]=cnt++;
	for(int i=1;i<=n;i++){
		if(op[i]==1){
			if(num[y[i]]==-1){
				for(auto it:v[y[i]])
					mp[min(it,x[i])][max(it,x[i])]++;
				v[y[i]].push_back(x[i]),sum[x[i]]++;
			}
			else  bs[x[i]][num[y[i]]]=1,sum[x[i]]++;
		}
		else{
			if(x[i]==y[i]){cout<<sum[x[i]]<<"\n";continue;}
			int res=sum[x[i]]+sum[y[i]]-mp[x[i]][y[i]];
			cout<<res-(bs[x[i]]&bs[y[i]]).count()<<"\n";
		}
	}
}

但是发现 MLE 死得很惨,考虑把哈希表和 bitset 都搞成指针,第一次插入的时候再分配内存。

于是我们获得了第二版代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
using namespace std;
using namespace __gnu_pbds;
#define gp gp_hash_table
const int N=1000005,B=176;
vector<int> v[N];
bitset<(N+B-1)/B> *bs[N];
gp_hash_table<int,int> *mp[N];
int n,cnt,sum[N],op[N],x[N],y[N],num[N],app[N];
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr),cout.tie(nullptr);
	cin>>n,fill(num,num+n+1,-1);
	for(int i=1;i<=n;i++){
		cin>>op[i]>>x[i]>>y[i];
		if(op[i]==1)  app[y[i]]++;
		if(op[i]==2&&x[i]>y[i])  swap(x[i],y[i]);
	}
	for(int i=1;i<=n;i++)  if(app[i]>=B)  num[i]=cnt++;
	for(int i=1;i<=n;i++){
		if(op[i]==1){
			if(num[y[i]]==-1){
				for(auto it:v[y[i]]){
					int tx=min(it,x[i]),ty=max(it,x[i]);
					if(!mp[tx])  mp[tx]=new gp<int,int>();
					(*mp[tx])[ty]++;
				}
				v[y[i]].push_back(x[i]),sum[x[i]]++;
			}
			else{
				if(!bs[x[i]])  bs[x[i]]=new bitset<(N+B-1)/B>();
				bs[x[i]]->set(num[y[i]]),sum[x[i]]++;
			}
		}
		else{
			if(x[i]==y[i]){cout<<sum[x[i]]<<"\n";continue;}
			int res=sum[x[i]]+sum[y[i]];
			if(mp[x[i]])  res-=(*mp[x[i]])[y[i]];
			if(bs[x[i]]&&bs[y[i]])  res-=(*bs[x[i]]&*bs[y[i]]).count();
			cout<<res<<"\n";
		}
	}
}

惊奇地发现现在又 TLE 了,注意到哈希表插入很慢,并且进行了很多无效插入。

所以先把所有询问放到哈希表里面,然后修改时判一下要不要改,这样就可以通过了。

于是我们获得了最终版代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
using namespace std;
using namespace __gnu_pbds;
const int N=1000005,B=176;
vector<int> v[N];
bitset<(N+B-1)/B> *bs[N];
gp_hash_table<int,int> *mp[N];
int n,cnt,sum[N],op[N],x[N],y[N],num[N],app[N];
signed main(){
	ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
	cin>>n,fill(num,num+n+1,-1);
	for(int i=1;i<=n;i++)  cin>>op[i]>>x[i]>>y[i],app[y[i]]+=(op[i]==1);
	for(int i=1;i<=n;i++)  if(app[i]>=B)  num[i]=cnt++;
	for(int i=1;i<=n;i++){
		if(op[i]==1)  continue;
		if(x[i]>y[i])  swap(x[i],y[i]);
		if(!mp[x[i]])  mp[x[i]]=new gp_hash_table<int,int>();
		(*mp[x[i]])[y[i]]=0;
	}
	for(int i=1;i<=n;i++){
		if(op[i]==1){
			if(num[y[i]]==-1){
				for(auto it:v[y[i]]){
					int tx=min(it,x[i]),ty=max(it,x[i]);
					if(mp[tx]&&mp[tx]->find(ty)!=mp[tx]->end())  (*mp[tx])[ty]++;
				}
				v[y[i]].push_back(x[i]),sum[x[i]]++;
			}
			else{
				if(!bs[x[i]])  bs[x[i]]=new bitset<(N+B-1)/B>();
				bs[x[i]]->set(num[y[i]]),sum[x[i]]++;
			}
		}
		else{
			if(x[i]==y[i]){cout<<sum[x[i]]<<"\n";continue;}
			int res=sum[x[i]]+sum[y[i]];
			if(bs[x[i]]&&bs[y[i]])  res-=(*bs[x[i]]&*bs[y[i]]).count();
			if(mp[x[i]]&&mp[x[i]]->find(y[i])!=mp[x[i]]->end())  res-=(*mp[x[i]])[y[i]];
			cout<<res<<"\n";
		}
	}
}

[Ynoi Easy Round 2015] 此时此刻的光辉

质因数分解+莫队,但是每次直接处理所有质因数会 TLE 得特别惨。

于是考虑把 \(\leq 1000\) 的所有质因数单独拿出来用前缀和处理,剩下的再用 gp_hash_table。

然后注意不能 #define int long long,还有最好可以使用奇偶性优化。

点击查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
using namespace std;
using namespace __gnu_pbds;
const int N=100005,M=32000,mod=19260817;
gp_hash_table<int,int> mp;
struct Node{int l,r,num;}s[N];
int res=1,L=1,R=0,sum[N][170],ans[N],bel[N];
int n,m,B,cnt,pos,a[N],inv[N<<1],p[M],pr[M],ag[N];
void add(int x){
	if(a[x]!=1){
		int tmp=mp[a[x]]++;
		res=1ll*res*inv[tmp+1]%mod*(tmp+2)%mod;
	}
	if(ag[x]){
		int tmp=mp[ag[x]]++;
		res=1ll*res*inv[tmp+1]%mod*(tmp+2)%mod;
	}
}
void del(int x){
	if(a[x]!=1){
		int tmp=mp[a[x]]--;
		if(!tmp)  mp.erase(a[x]);
		res=1ll*res*inv[tmp+1]%mod*tmp%mod;
	}
	if(ag[x]){
		int tmp=mp[ag[x]]--;
		if(!tmp)  mp.erase(ag[x]);
		res=1ll*res*inv[tmp+1]%mod*tmp%mod;
	}
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr),cout.tie(nullptr);
	cin>>n>>m,inv[0]=inv[1]=1,B=sqrt(n);
	for(int i=2;i<=(n<<1);i++)
		inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod;
	for(int i=1;i<=n;i++)  bel[i]=(i-1)/B;
	for(int i=2;i<=M-5;i++){
		if(!p[i])  pr[++cnt]=i;
		for(int j=1;j<=cnt&&i*pr[j]<=M-5;j++){
			p[i*pr[j]]=1;
			if(i%pr[j]==0)  break;
		}
	}
	while(pr[pos+1]<=1000)  pos++;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		for(int j=1;j<=pos;j++){
			sum[i][j]=sum[i-1][j];
			while(a[i]%pr[j]==0)  a[i]/=pr[j],sum[i][j]++;
		}
		for(int j=pos+1;pr[j]*pr[j]<=a[i];j++){
			if(a[i]%pr[j]!=0)  continue;
			ag[i]=pr[j],a[i]/=ag[i];break;
		}
	}
	for(int i=1;i<=m;i++)  cin>>s[i].l>>s[i].r,s[i].num=i;
	sort(s+1,s+m+1,[](Node a,Node b){
		if(bel[a.l]!=bel[b.l])  return bel[a.l]<bel[b.l];
		return bel[a.l]&1?a.r>b.r:a.r<b.r;
	});
	for(int i=1;i<=m;i++){
		while(L>s[i].l)  add(--L);
		while(R<s[i].r)  add(++R);
		while(L<s[i].l)  del(L++);
		while(R>s[i].r)  del(R--);
		ans[s[i].num]=res;
		for(int j=1;j<=pos;j++){
			int len=sum[R][j]-sum[L-1][j]+1;
			ans[s[i].num]=1ll*ans[s[i].num]*len%mod;
		}
	}
	for(int i=1;i<=m;i++)  cout<<ans[i]<<"\n";
}

[Cnoi2019] 数字游戏

首先有一个很容易想到的 \(O(n\sqrt{n}\log n)\) 的做法,即用值域莫队+线段树直接解决。

但是这显然过不了,考虑把线段树换成分块,在块内维护极大连续的出现区间。

具体方法:在每个极大区间的左右断点处维护所在区间,并维护每个区间的左右端点。

然后这样因为不知道每个数具体在哪个区间,所以值域莫队改为回滚解决。

写的时候注意一下细节,还有就是回滚莫队要和分块一样特判同块不然会死得很惨。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define N 200005
#define int long long
stack<pair<int&,int>> st;
struct Node{int l,r,x,y,num;}s[N];
int n,q,k,B,a[N],p[N],bl[N],br[N],bel[N];
int cnt,cov[N],dl[N],dr[N],dy[N],sum[N],ans[N];
int calc(int x){return x*(x+1)/2;}
int getsize(int x){return !x?0:dr[x]-dl[x]+1;}
void change(int &x,int y){st.emplace(x,x),x=y;}
void add(int x){
	change(cov[p[x]],1);
	bool mrl=(bel[p[x]]==bel[p[x]-1]&&dy[p[x]-1]);
	bool mrr=(bel[p[x]]==bel[p[x]+1]&&dy[p[x]+1]);
	if(mrl&&mrr){
		int res=sum[bel[p[x]]];
		res-=calc(getsize(dy[p[x]-1]));
		res-=calc(getsize(dy[p[x]+1]));
		change(dr[dy[p[x]-1]],dr[dy[p[x]+1]]);
		change(dy[dr[dy[p[x]+1]]],dy[p[x]-1]);
		res+=calc(getsize(dy[p[x]-1]));
		change(sum[bel[p[x]]],res);
	}
	if(mrl&&!mrr){
		int res=sum[bel[p[x]]];
		res-=calc(getsize(dy[p[x]-1]));
		change(dr[dy[p[x]-1]],p[x]);
		change(dy[p[x]],dy[p[x]-1]);
		res+=calc(getsize(dy[p[x]]));
		change(sum[bel[p[x]]],res);
	}
	if(!mrl&&mrr){
		int res=sum[bel[p[x]]];
		res-=calc(getsize(dy[p[x]+1]));
		change(dl[dy[p[x]+1]],p[x]);
		change(dy[p[x]],dy[p[x]+1]);
		res+=calc(getsize(dy[p[x]]));
		change(sum[bel[p[x]]],res);
	}
	if(!mrl&&!mrr){
		change(cnt,cnt+1),change(dy[p[x]],cnt);
		change(sum[bel[p[x]]],sum[bel[p[x]]]+1);
		change(dl[cnt],p[x]),change(dr[cnt],p[x]);
	}
}
int query(int l,int r){
	int res=0,sum=0;
	if(bel[l]==bel[r]){
		for(int i=l;i<=r;i++){
			if(cov[i])  sum++;
			else  res+=calc(sum),sum=0;
		}
		return res+calc(sum);
	}
	for(int i=l;i<=br[bel[l]];i++){
		if(cov[i])  sum++;
		else  res+=calc(sum),sum=0;
	}
	for(int i=bel[l]+1;i<bel[r];i++){
		res+=::sum[i];
		if(!cov[bl[i]]){
			res+=calc(sum);
			res-=calc(sum=getsize(dy[br[i]]));
		}
		else if(dr[dy[bl[i]]]==br[i])
			sum+=br[i]-bl[i]+1,res-=::sum[i];
		else{
			res+=calc(sum+getsize(dy[bl[i]]));
			sum=getsize(dy[br[i]]);
			res-=calc(getsize(dy[bl[i]]))+calc(sum);
		}
	}
	for(int i=bl[bel[r]];i<=r;i++){
		if(cov[i])  sum++;
		else  res+=calc(sum),sum=0;
	}
	return res+calc(sum);
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr),cout.tie(nullptr);
	cin>>n>>q,B=n/sqrt(n)+1,k=n/B+bool(n%B);
	for(int i=1;i<=n;i++)  cin>>a[i],p[a[i]]=i;
	for(int i=1,l,r,x,y;i<=q;i++)
		cin>>l>>r>>x>>y,s[i]={l,r,x,y,i};
	for(int i=1;i<=n;i++)  bel[i]=(i+B-1)/B;
	for(int i=1;i<=k;i++)  bl[i]=(i-1)*B+1,br[i]=i*B;
	sort(s+1,s+q+1,[](Node x,Node y){
		if(bel[x.x]==bel[y.x])  return x.y<y.y;
		return bel[x.x]<bel[y.x];
	});
	for(int i=1,L,R;i<=q;i++){
		if(bel[s[i].x]!=bel[s[i-1].x]){
			R=br[bel[s[i].x]];
			while(!st.empty())
			{auto [x,y]=st.top();x=y,st.pop();}
		}
		if(bel[s[i].x]==bel[s[i].y]){
			for(int j=s[i].x;j<=s[i].y;j++)  add(j);
			ans[s[i].num]=query(s[i].l,s[i].r);
			while(!st.empty())
			{auto [x,y]=st.top();x=y,st.pop();}
			continue;
		}
		while(R<s[i].y)  add(++R);
		int tmp=st.size();L=br[bel[s[i].x]]+1;
		while(L>s[i].x)  add(--L);
		ans[s[i].num]=query(s[i].l,s[i].r);
		while(st.size()>tmp)
		{auto [x,y]=st.top();x=y,st.pop();}
	}
	for(int i=1;i<=q;i++)  cout<<ans[i]<<"\n";
}

[CF1476G] Minimum Difference

带修莫队,注意到 \(cnt_i\)\(cc_i\) 分别表示 \(i\)\(cnt\) 值中 \(i\) 的出现次数是好维护的。

然后只要搞出来有值的 \(cc_i\)(最多有 \(\sqrt{n}\) 个)有哪些就可以用双指针很方便地维护了。

具体维护考虑对序列分块,如果块内有值就遍历块内提取有用值装进 vector 里。

这样的复杂度是 \(O(\frac{n\sqrt{B}}{B})\),当 \(B\)\(n^{\frac{2}{3}}\) 时最优,此时刚好也是带修莫队的最优块长。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=100005,INF=0x3f3f3f3f;
struct Chan{int p,x,y;}c[N];
struct Node{int l,r,k,t,i;}s[N];
int L=1,R,T,B,sum[N],bl[N],br[N],bel[N];
int n,m,k,q,t,a[N],b[N],cnt[N],cc[N],ans[N];
void add(int x){
	cc[cnt[x]]--,sum[bel[cnt[x]]]--;
	cc[++cnt[x]]++,sum[bel[cnt[x]]]++;
}
void del(int x){
	cc[cnt[x]]--,sum[bel[cnt[x]]]--;
	cc[--cnt[x]]++,sum[bel[cnt[x]]]++;
}
void upd(int p,int x){
	if(L<=p&&p<=R)  del(a[p]),add(x);a[p]=x;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr),cout.tie(nullptr);
	cin>>n>>m,B=pow(n,2.0/3),k=n/B+bool(n%B);
	for(int i=1;i<=n;i++)  cin>>a[i],b[i]=a[i];
	for(int i=1;i<=n;i++)  bel[i]=(i+B-1)/B;
	for(int i=1;i<=k;i++)  bl[i]=(i-1)*B+1;
	for(int i=1;i<=k;i++)  br[i]=min(i*B,n);
	for(int i=1,op,x,y,k;i<=m;i++){
		cin>>op>>x>>y;
		if(op==2)  c[++t]={x,b[x],y},b[x]=y;
		else  cin>>k,q++,s[q]={x,y,k,t,q};
	}
	sort(s+1,s+q+1,[](Node x,Node y){
		if(bel[x.l]!=bel[y.l])  return x.l<y.l;
		if(bel[x.r]!=bel[y.r])  return x.r<y.r;
		return x.t<y.t;
	});
	for(int i=1;i<=q;i++){
		while(L>s[i].l)  add(a[--L]);
		while(R<s[i].r)  add(a[++R]);
		while(L<s[i].l)  del(a[L++]);
		while(R>s[i].r)  del(a[R--]);
		while(T<s[i].t)  T++,upd(c[T].p,c[T].y);
		while(T>s[i].t)  upd(c[T].p,c[T].x),T--;
		int &ans=(::ans[s[i].i]=INF),now=0,res=0;
		vector<int> num(0,0),sum(0,0);
		for(int j=1;j<=k;j++){
			if(!::sum[j])  continue;
			for(int k=bl[j];k<=br[j];k++){
				if(!cc[k])  continue;
				num.push_back(k),sum.push_back(cc[k]);
			}
		}
		for(int l=0,r=0;l<num.size();res-=sum[l++]){
			while(r<num.size()&&res<s[i].k)  res+=sum[r++];
			if(res>=s[i].k)  ans=min(ans,num[r-1]-num[l]);
		}
	}
	for(int i=1;i<=q;i++)  cout<<(ans[i]==INF?-1:ans[i])<<"\n";
}

[Ynoi2013] 文化课

如果没有修改的话用普通的线段树是容易进行维护的。

只有操作二的时候多维护两个值表示区间内加和和求积也是容易的。

但是操作一成功让这道题成为了毒瘤。

不过如果我们知道一个区间内乘法段的分布过后信息也是容易维护的。

所以考虑维护乘法段,用 vector 记录区间内有多少种长度的乘法段及每种的个数。

容易得到每个节点的 vector 上最多只有 \(\sqrt{n}\) 个信息,合并用归并即可。

额外要注意的是 \(x\) 取模后可能是 \(0\),而且直接快速幂会超时。

在区间覆盖值时的计算答案的快速幂要每个在之前值的基础上继续乘才能在时限内通过。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=100005,mod=1000000007;
int n,m,a[N],op[N];
int quick_pow(int x,int y,int res=1){
	for(;y;x=x*x%mod,y>>=1)  if(y&1)  res=res*x%mod;
	return res;
}
struct Segment_tree{
	vector<int> d[N<<1],ds[N<<1];int top[N<<1],tum[N<<1];
	struct tree{int op,lub,lum,rub,rum,len,res,add,mul,lc,rc;}tr[N<<1];
	tree pushup(tree ls,tree rs){
		tree res={rs.op,0,0,0,0,0,0,0,0,ls.lc,rs.rc};
		res.len=ls.len+rs.len,res.res=(ls.res+rs.res)%mod;
		if(ls.lub!=ls.len||!ls.op)  res.lub=ls.lub,res.lum=ls.lum;
		else  res.lub=ls.lub+rs.lub,res.lum=ls.lum*rs.lum%mod;
		if(rs.rub!=rs.len||!ls.op)  res.rub=rs.rub,res.rum=rs.rum;
		else  res.rub=ls.rub+rs.rub,res.rum=ls.rum*rs.rum%mod;
		res.add=(ls.add+rs.add)%mod,res.mul=ls.mul*rs.mul%mod;
		if(ls.op)  res.res=(res.res-ls.rum-rs.lum+ls.rum*rs.lum+mod*2)%mod;
		return res;
	}
	void erase(vector<int> &v,vector<int> &vs,int x){
		for(int i=0;i<v.size();i++){
			if(v[i]!=x||--vs[i])  continue;
			vs.erase(vs.begin()+i),v.erase(v.begin()+i);break;
		}
	}
	void insert(vector<int> &v,vector<int> &vs,int x){
		int pos=lower_bound(v.begin(),v.end(),x)-v.begin();
		if(pos==v.size())  v.push_back(x),vs.push_back(1);
		else if(v[pos]==x)  vs[pos]++,void();
		else  v.insert(v.begin()+pos,x),vs.insert(vs.begin()+pos,1);
	}
	void pushupv(int &p,int &ls,int &rs){
		d[p].clear(),ds[p].clear();
		if(tr[ls].op){
			erase(d[ls],ds[ls],tr[ls].rub);
			erase(d[rs],ds[rs],tr[rs].lub);
			insert(d[ls],ds[ls],tr[ls].rub+tr[rs].lub);
		}
		int lsz=d[ls].size(),rsz=d[rs].size();
		for(int i=0,j=0;i<lsz||j<rsz;){
			if(i<lsz&&j<rsz&&d[ls][i]==d[rs][j]){
				d[p].push_back(d[ls][i]);
				ds[p].push_back(ds[ls][i]+ds[rs][j]),i++,j++;
			}
			else if(j==rsz||i<lsz&&d[ls][i]<d[rs][j])
				d[p].push_back(d[ls][i]),ds[p].push_back(ds[ls][i++]);
			else
				d[p].push_back(d[rs][j]),ds[p].push_back(ds[rs][j++]);
		}
		if(tr[ls].op){
			insert(d[ls],ds[ls],tr[ls].rub);
			insert(d[rs],ds[rs],tr[rs].lub);
			erase(d[ls],ds[ls],tr[ls].rub+tr[rs].lub);
		}
	}
	void worknum(int &p,int &x){
		int now=quick_pow(tum[p]=tr[p].lc=tr[p].rc=x,d[p][0]);
		tr[p].res=now*ds[p][0]%mod,tr[p].add=x*tr[p].len%mod;
		if(d[p][0]==tr[p].lub)  tr[p].lum=now;
		if(d[p][0]==tr[p].rub)  tr[p].rum=now;
		for(int i=1;i<d[p].size();i++){
			now=now*quick_pow(x,d[p][i]-d[p][i-1])%mod;
			if(d[p][i]==tr[p].lub)  tr[p].lum=now;
			if(d[p][i]==tr[p].rub)  tr[p].rum=now;
			tr[p].res=(tr[p].res+now*ds[p][i])%mod;
		}
		tr[p].mul=now*quick_pow(x,tr[p].len-d[p].back())%mod;
	}
	void workop(int &p,int op){
		tr[p].op=top[p]=op;
		if(!op){
			tr[p].lub=tr[p].rub=1,tr[p].lum=tr[p].lc;
			tr[p].rum=tr[p].rc,tr[p].res=tr[p].add;
			d[p].clear(),d[p].push_back(1);
			ds[p].clear(),ds[p].push_back(tr[p].len);
		}
		else{
			tr[p].lub=tr[p].rub=tr[p].len;
			tr[p].lum=tr[p].rum=tr[p].res=tr[p].mul;
			d[p].clear(),d[p].push_back(tr[p].len);
			ds[p].clear(),ds[p].push_back(1);
		}
	}
	void pushdown(int &p,int &ls,int &rs){
		if(top[p]!=-1)  workop(ls,top[p]),workop(rs,top[p]);
		if(tum[p]!=-1)  worknum(ls,tum[p]),worknum(rs,tum[p]);
		tum[p]=-1,top[p]=-1;
	}
	void build(int l=1,int r=n,int p=1){
		if(l==r){
			tr[p]={op[l],1,a[l],1,a[l],1,a[l],a[l],a[l],a[l],a[l]};
			return d[p].push_back(1),ds[p].push_back(1),void();
		}
		int mid=(l+r)>>1,ls=mid<<1,rs=mid<<1|1;
		build(l,mid,ls),build(mid+1,r,rs);
		tr[p]=pushup(tr[ls],tr[rs]),pushupv(p,ls,rs);
	}
	void updatenum(int &sl,int &sr,int &x,int l=1,int r=n,int p=1){
		if(sl<=l&&r<=sr)  return worknum(p,x);
		int mid=(l+r)>>1,ls=mid<<1,rs=mid<<1|1;pushdown(p,ls,rs);
		if(sl<=mid)  updatenum(sl,sr,x,l,mid,ls);
		if(sr>mid)  updatenum(sl,sr,x,mid+1,r,rs);
		tr[p]=pushup(tr[ls],tr[rs]);
	}
	void updateop(int &sl,int &sr,int &x,int l=1,int r=n,int p=1){
		if(sl<=l&&r<=sr)  return workop(p,x);
		int mid=(l+r)>>1,ls=mid<<1,rs=mid<<1|1;pushdown(p,ls,rs);
		if(sl<=mid)  updateop(sl,sr,x,l,mid,ls);
		if(sr>mid)  updateop(sl,sr,x,mid+1,r,rs);
		tr[p]=pushup(tr[ls],tr[rs]),pushupv(p,ls,rs);
	}
	tree query(int &sl,int &sr,int l=1,int r=n,int p=1){
		if(sl<=l&&r<=sr)  return tr[p];
		int mid=(l+r)>>1,ls=mid<<1,rs=mid<<1|1;pushdown(p,ls,rs);
		if(sr<=mid)  return query(sl,sr,l,mid,ls);
		if(sl>mid)  return query(sl,sr,mid+1,r,rs);
		return pushup(query(sl,sr,l,mid,ls),query(sl,sr,mid+1,r,rs));
	}
	void init(){fill(top,top+(N<<1),-1),fill(tum,tum+(N<<1),-1);}
}SGT;
signed main(){
	ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
	cin>>n>>m;
	for(int i=1;i<=n;i++)  cin>>a[i],a[i]%=mod;
	for(int i=1;i<n;i++)  cin>>op[i];
	SGT.init(),SGT.build();
	for(int i=1,op,l,r,x;i<=m;i++){
		cin>>op>>l>>r;
		if(op==1)  cin>>x,SGT.updatenum(l,r,x%=mod);
		if(op==2)  cin>>x,SGT.updateop(l,r,x);
		if(op==3)  cout<<SGT.query(l,r).res<<"\n";
	}
}
posted @ 2026-01-27 20:47  tkdqmx  阅读(32)  评论(0)    收藏  举报