【清华集训2014】【线段树】玄学

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

【描述】
巨酱有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";
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值