CF 292E 题解
提供一个比较简单的做法。
对于每一次覆盖,不用线段树直接去存两端点 \([l,r]\),可以存下来这次修改对应第几次覆盖(未被覆盖为 \(0\)),查找时再根据存储的次数去输出。
最后输出时如果未被覆盖,直接输出 \(b[i]\),如果有覆盖次数 \(num\),则对应数组 \(A\) 的下标为:(\(X\),\(Y\) 为对应 \(A\) 和 \(B\) 该次覆盖的下标)
\[(i-x[num]+1)+y[num]-1=i-x[num]+y[num]
\]
时间复杂度 \(O(m\log{n})\),可以通过本题。
#include <iostream>
#include <cstdio>
#define SIZE 131072
#define ll long long
using namespace std;
int x[100005], y[100005], w[SIZE*2+5], tag[SIZE*2+5], cnt, n, m, a[100005], b[100005];
//线段树
void build(ll p, ll l, ll r){
tag[p]=0;
if(l==r){
w[p]=0;
return;
}
int mid=(l+r)>>1;
build(p*2, l, mid);
build(p*2+1, mid+1, r);
w[p]=w[p*2]+w[p*2+1];
}
void f(ll p, ll l, ll r, ll k){
//下推的时候注意,如果k=0(没有)就不要往下放,要不然会覆盖掉原有的标记(被卡了好久)
if(k!=0) tag[p]=k;
if(k!=0) w[p]=(r-l+1)*k;
}
void update(ll p, ll nl, ll nr, ll l, ll r, ll k){
if(nl<=l&&nr>=r){
tag[p]=k;
w[p]=(r-l+1)*k;
return;
}
ll mid=(l+r)>>1;
f(p*2, l, mid, tag[p]);
f(p*2+1, mid+1, r, tag[p]);
tag[p]=0;
if(nl<=mid) update(p*2, nl, nr, l, mid, k);
if(nr>mid) update(p*2+1, nl, nr, mid+1, r, k);
w[p]=w[p*2]+w[p*2+1];
}
ll query(ll p, ll nl, ll nr, ll l, ll r){
ll res=0;
if(nl<=l&&nr>=r) return w[p];
//下放到下面一层
ll mid=(l+r)>>1;
f(2*p, l, mid, tag[p]);
f(2*p+1, mid+1, r, tag[p]);
tag[p]=0;
if(nl<=mid) res+=query(2*p, nl, nr, l, mid);
if(nr>mid) res+=query(2*p+1, nl, nr, mid+1, r);
return res;
}
int main(){
scanf("%d%d", &n, &m);
for (int i=1; i<=n; i++) scanf("%d", &b[i]);
for (int i=1; i<=n; i++) scanf("%d", &a[i]);
build(1, 1, n);
while (m--){
long long cmd;
scanf("%lld", &cmd);
if(cmd==1){
ll z;++cnt;
scanf("%d%d%lld", &y[cnt], &x[cnt], &z);
//更新时范围是从起点到起点+z-1(输入时数组写反了)
update(1, x[cnt], x[cnt]+z-1, 1, n, cnt);
}
else{
int u;scanf("%d", &u);
ll num=query(1, u, u, 1, n);
//分情况输出
if(num) printf("%d\n", b[u-x[num]+y[num]]);
else printf("%d\n", a[u]);
}
}
return 0;//完结撒花
}