【描述】
巨酱有n副耳机,他把它们摆成了一列,并且由1到n依次编号。每个耳机有一个玄学值,反映了各自的一些不可名状的独特性能。玄学值都是0到m−1间的整数。在外界的作用下(包括但不限于换线、上放、更换电源为核电、让kAc叔叔给它们讲故事),这些耳机的玄学值会发生改变。特别地,巨酱观察发现,每种作用o对应了两个整数ao与bo,在这种作用之后,玄学值原本为x的耳机,其玄学值恰会变成(ao*x+bo)modm。
巨酱对他手头耳机的表现并不满意,遗憾的是,最近他并不有钱,无法任性,不能赶紧买买买以满足自己。手头紧张的他准备拟定一个相对经济的方案,通过各种作用来改善他手头玩具的性能。具体地说,为了尽快完成方案的制订,巨酱希望自己能高效地完成以下工作:
1、巨酱想到了一种操作,能让耳机的玄学值由x变为(ax+b)modm,并且他计划对编号为i到j的耳机执行这种操作。
2、巨酱想知道如果将(并且仅将)自己的第i个到第j个计划按顺序付诸行动,编号为k的耳机的玄学值将会变成多少。
出于著名算法竞赛选手的矜持,巨酱表示自己才不需要你的帮助。但是如果巨酱真的厌倦了自己的玩具,它们就会被50包邮出给主席。为了不让后者白白捡到便宜,你考虑再三还是决定出手。
【思路】
这道题思路很神奇。显然,对于若干次修改,由于修改满足结合律,我们可以把它们合并在一起。所以一个自然的思路就是二进制拆分。但是二进制拆分不一定恰好能把[i,j][i,j][i,j]拆出来。所以我们考虑使用线段树来实现修改的合并。如果这是第tot次修改,我们就把它记录在线段树第tot个叶节点处。如果一个节点的左右儿子都填满了,那么就把左右儿子的修改合并到当前直接点,类似于二进制拆分的进位,但是我们保留了子节点的信息,可以理解为一种可持久化的二进制拆分。假设我们在一个叶节点新加了一个修改(l,r,a,b),我们考虑维护变换(a,b)表示x=ax+b。那么这个节点的维护的修改的贡献就是(1,l-1,1,0),(l,r,a,b),(r+1,n,1,0)。合并操作类似归并排序。查询就是普通的区间查询。
代码:
#include<bits/stdc++.h>
#define int long long
#define re register
#define lc (p<<1)
#define rc (p<<1|1)
using namespace std;
const int N=1e6+5;
inline int red(){
int data=0;bool w=0; char ch=getchar();
while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
if(ch=='-') w=1,ch=getchar();
while(ch>='0' && ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
return w?-data:data;
}
int n,a[N>>2|1],b,c,typ,mod,las=0,tot=0;
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|1];
inline void merge(vector<node>&nw,vector<node>L,vector<node>R){
unsigned int l=0,r=0;
while(l<L.size()&&r<R.size()){
nw.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;
}
}
void change(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)change(lc,l,mid,x,y,a,b);
else change(rc,mid+1,r,x,y,a,b);
if(tot==r)merge(t[p],t[lc],t[rc]);
}
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();
las=add(mul(las,t[p][x].a),t[p][x].b);return;
}int mid=(l+r)>>1;
if(ql<=mid)query(lc,l,mid,ql,qr,pos);
if(qr>mid)query(rc,mid+1,r,ql,qr,pos);
}
signed main()
{
typ=red()&1;n=red();mod=red();
for(int re i=1;i<=n;i++)a[i]=red();
int m=red();int all=m;
while(m--){
int op=red(),l=red(),r=red();
if(typ)l^=las,r^=las;
if(op==1){
b=red()%mod,c=red()%mod;++tot;
change(1,1,all,l,r,b,c);
}else{
int pos=red();
if(typ)pos^=las;las=a[pos];
query(1,1,all,l,r,pos);
cout<<las<<"\n";
}
}
}

巨酱面临耳机性能提升的挑战,利用线段树和二进制拆分技巧,实现耳机玄学值的有效调整。算法巧妙地处理耳机在特定作用下玄学值的变化,通过合并操作减少计算复杂度。

439

被折叠的 条评论
为什么被折叠?



