BSOJ3214 UOJ46【清华集训2014】玄学

题目

给出很多个形如:\((l,r,a,b)\) 的操作,表示让区间 \([l,r]\) 当中的数变成 \(x\to (ax+b)\mod p\)

(其中 \(p\) 是初始给定的。)

然后多次询问 \((l,r,pos)\) 表示询问:如果执行区间 \([l,r]\) 当中的操作,那么第 \(pos\) 个数的值是多少。

分析

先考虑没有第二个区间限制(就是查询当前的 \(a[pos]\) 的值),有一个很奇怪的做法:

对于每一个操作,分成三个这样的部分:\((1,0,l-1),(a,b,r),(1,0,n)\) ,每一个操作\((a,b,pos)\)的意思是:对于 \([laspos+1,pos]\) 的所有数都有变化:\(x\to (ax+b)\mod p\)

然后我们对所有的操作二进制分组,容易发现合并(修改部分)的复杂度是 \(O(n\log n)\) 的,因为就算把所有操作合并起来,也就只会把原序列分成 \(O(n)\) 级别段。(就类似是差分,合并就是差分标记的合并,具体可以见代码)

接下来考虑查询。

发现其实就是从当前的二进制分组的vector(里面保存的是操作合并后)里从左往右进行查询,也就是在每一个里面二分 \(pos\) 的位置,找到当前这个vector当中第一个位置大于等于 \(pos\) 的,然后对 \(ans=a[pos]\) 进行这个节点对应的 \((a,b)\) 的操作。

这样做是 \(O(m\log^2 n)\) 的。

然后考虑有区间限制的做法。

考虑线段树维护二进制分组的过程,于是查询就变成了线段树上递归下去的 \(\log\) 个区间,修改就是正常的二进制分组,只不过用了线段树维护。

然后就结束了,时间复杂度 \(O(n\log n+m\log^2 n)\)

代码

#include<bits/stdc++.h>
using namespace std;
inline char gc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
template <typename T>
inline void read(T &x){
	x=0;char ch=gc();bool f=false;
	while(!isdigit(ch)){if(ch=='-'){f=true;}ch=gc();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
	x=f?-x:x;
	return ;
}
template <typename T>
inline void write(T x){
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);
	putchar(x%10^48);
	return ;
}
#define int long long
const int N=1e6+5;
int n,m,a[N],typ,MOD,tot,Ans;
inline int mul(const int &a,const int &b){return 1ll*a*b%MOD;}
inline int add(const int &a,const int &b){return a+b>=MOD?a+b-MOD:a+b;}
struct node{
	int a,b,r;
	node(int x=0,int y=0,int z=0){a=x,b=y,r=z;}
	friend inline node operator + (const node &a,const node &b){return node(mul(a.a,b.a),add(mul(a.b,b.a),b.b),min(a.r,b.r));}
	friend inline bool operator < (const node &a,const node &b){
		if(a.r^b.r) return a.r<b.r;
		if(a.a^b.a) return a.a<b.a;
		return a.b<b.b;
	}
};
vector<node>t[N<<2];
inline void Merge(vector<node>&Now,vector<node>L,vector<node>R){//合并两个操作序列
	unsigned int l=0,r=0;
	while(l<L.size()&&r<R.size()){
		Now.push_back(L[l]+R[r]);
		if(L[l].r==R[r].r) l++,r++;
		else if(R[r].r<L[l].r) r++;
		else l++;
	}
	return ;
}
void Modify(int p,int l,int r,const int &x,const int &y,const int &a,const int &b){
	if(l==r){
		t[p].push_back(node(1,0,x-1));
		t[p].push_back(node(a,b,y));
		if(y^n) t[p].push_back(node(1,0,n));
		return;
	}
	int mid=(l+r)>>1;
	if(tot<=mid) Modify(p<<1,l,mid,x,y,a,b);
	else Modify(p<<1|1,mid+1,r,x,y,a,b);
	if(tot==r) Merge(t[p],t[p<<1],t[p<<1|1]);
	return ;
}
void Query(int p,int l,int r,int ql,int qr,int pos){
	if(ql<=l&&qr>=r){
		int x=lower_bound(t[p].begin(),t[p].end(),node(0,0,pos))-t[p].begin();
		Ans=add(mul(Ans,t[p][x].a),t[p][x].b);return;
	}
	int mid=(l+r)>>1;
	if(ql<=mid) Query(p<<1,l,mid,ql,qr,pos);
	if(qr>mid) Query(p<<1|1,mid+1,r,ql,qr,pos);
	return ;
}
signed main(){
	read(typ),typ=typ&1,read(n),read(MOD);
	for(int i=1;i<=n;i++) read(a[i]);read(m);
	for(int i=1;i<=m;i++){
		int op,l,r,b,c;
		read(op),read(l),read(r);
		if(typ) l^=Ans,r^=Ans;
		if(op==1){
			read(b),read(c);
			b%=MOD,c%=MOD;++tot;
			Modify(1,1,m,l,r,b,c);
		}
		else{
			int pos;read(pos);
			if(typ) pos^=Ans;Ans=a[pos];
			Query(1,1,m,l,r,pos);
			write(Ans),putchar('\n');
		}
	}
	return 0;
}

posted @ 2021-10-09 16:00  __Anchor  阅读(77)  评论(0)    收藏  举报