/*题意:
给出一个N个数的序列以及一个k(0<k<=n<=200000),m个操作p,x,y,其中
p=0:将x位置的数替换为y
p=1:将x y位置的数互换
p=2: 查询x-y位置区间连续k个数的和的最大值
分析:因为要求连续区间的最大,我们可以把区间和当做一个节点,这样区间就代表一个点,点就代表一个区间了,这样就可以把原来1~n个数重新分为1~n-k+1了,所以我们只要建一颗总节点范围为1~n-k+1的线段树,p=0时,他能影响到的区间为[min(1,a-k+1),max(n-k+1,b),这个区间的最大值都变成了maxx+=b-v[a];p=1的类似。p=2时,询问的区间为
a~b-k+1;
最关键的就是线段树的优化,lazy操作。当前我的理解就是:更新的时候如果当前满足的要求,标记lazy,不在往下更新,当不符合要求的时候才继续往下update,并且更新子节点的时候回来必须更新父节点。询问的时候,当符合要求的时候就返回当父节点的值,不符合的时候才往下更新知道可以知道结果就停止了。这样就能达到了不需要更新的就不更新的效果,从而优化了。*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include<iostream>
using namespace std;
const int Nmax=200080;
int n,m,k,sum;
int v[Nmax];
int b[Nmax];
struct Node
{
int L;
int R;
int maxx;
int lazy;//父节点还没有释放的值
}tree[Nmax*10];
int builttree(int fa,int LL,int RR)//返回根的最大值
{
int mid=(LL+RR)/2;
tree[fa].L=LL;
tree[fa].R=RR;
tree[fa].lazy=0;
if(LL==RR)
{
tree[fa].maxx=b[mid];
return b[LL];
}
int t=builttree(fa*2,LL,mid);
int r=builttree(fa*2+1,mid+1,RR);
tree[fa].maxx=max(r,t);//父节点的最大值在建完子树之后才能求出
return tree[fa].maxx;
}
void down(int fa)//向下更新
{
if(tree[fa].L==tree[fa].R)
return ;
tree[fa*2].lazy+=tree[fa].lazy;
tree[fa*2].maxx+=tree[fa].lazy;
tree[fa*2+1].lazy+=tree[fa].lazy;
tree[fa*2+1].maxx+=tree[fa].lazy;
tree[fa].lazy=0;
}
void up(int fa)//向上更新
{
if(tree[fa].L==tree[fa].R)return ;
tree[fa].maxx=max(tree[fa*2].maxx,tree[fa*2+1].maxx);
}
void update(int fa,int LL,int RR,int val)
{
if(tree[fa].L==LL&&tree[fa].R==RR)
{
tree[fa].maxx+=val;
tree[fa].lazy+=val;
return ;
}
if(tree[fa].lazy)
down(fa);//父节点在上次中还没被向下释放的值
int mid=(tree[fa].L+tree[fa].R)/2;
if(RR<=mid)
update(fa*2,LL,RR,val);
else if(LL>mid)
update(fa*2+1,LL,RR,val);
else
{
update(fa*2,LL,mid,val);
update(fa*2+1,mid+1,RR,val);
}
up(fa);//必须修改他父亲的值
}
int query(int fa,int LL,int RR)
{
if(tree[fa].L==LL&&tree[fa].R==RR)
return tree[fa].maxx;
if(tree[fa].lazy)
down(fa);
int mid=(tree[fa].L+tree[fa].R)/2;
if(RR<=mid)
return query(fa*2,LL,RR);
else if(LL>mid)
return query(fa*2+1,LL,RR);
else
{
int r= query(fa*2,LL,mid);
int t= query(fa*2+1,mid+1,RR);
return max(r,t);
}
}
int main()
{
int cas;
scanf("%d",&cas);
while(cas--)
{
//cin>>n>>m>>k;
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++)
//cin>>v[i];
scanf("%d",&v[i]);
sum=n-k+1;
memset(b,0,sizeof(b));
for(int i=1;i<=k;i++)
b[1]+=v[i];
for(int i=2;i<=n-k+1;i++)
b[i]=b[i-1]-v[i-1]+v[i+k-1];
// for(int i=1;i<=n;i++)
// cout<<b[i]<<' ';
// cout<<endl;
int aa=builttree(1,1,n-k+1);
// cout<<aa<<endl;
// for(int i=1;i<=n;i++)
// cout<<tree[i].maxx<<' ';
// cout<<endl;
int com;
int a,b;
for(int i=0;i<m;i++)
{
//cin>>com>>a>>b;
scanf("%d%d%d",&com,&a,&b);
if(com==0)
{
update(1,max(1,a-k+1),min(sum,a),b-v[a]);//注意区间,不要越b数组界
v[a]=b;
}
else if(com==1)
{
update(1,max(1,a-k+1),min(a,sum),v[b]-v[a]);
update(1,max(1,b-k+1),min(sum,b),v[a]-v[b]);
swap(v[a],v[b]);
}
else if(com==2)
{
int ans=query(1,a,b-k+1);
//cout<<ans<<endl;
printf("%d\n",ans);
}
}
}
return 0;
}