Solution
因为后面的操作不会对前面的操作有影响,所以我们考虑把操作倒过来。相应的,一开始就只有 \(k\) 个位置有效,然后对于操作 \([l,r]\) ,就把 \([r+1,r+(r-l+1)]\) 和 \([1,k]\) 的交集标记为已操作,并记录每一位是哪一位转移过来的。然后对于前一个操作,只用修改那些未标记的位置。
最多有 \(k\) 位,所以最多只要 \(k\) 次。具体记录位置可以用线段树+二分来寻找。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ls o<<1
#define rs o<<1|1
using namespace std;
const int N = 3e6 + 10;
int k, n, l[N], r[N], tr[N * 4], f[N];
char s[N], ans[N];
void build(int o,int l,int r){
tr[o]=r-l+1;
if(l==r) return ;
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
}
int find(int o,int l,int r,int k,int p){
while(l!=r){
tr[o]-=p;
int mid=(l+r)>>1;
if(tr[ls]>=k) r=mid,o<<=1;
else k-=tr[ls],o=rs,l=mid+1;
}
tr[o]-=p;
return l;
}
void work(){
for(int i=n;i>=1;i--){
if(r[i]>=tr[1]) continue;
int t=l[i]&1^1;
int now=l[i]+t;
if(now>r[i]) now=l[i]+(t^1);
for(int j=1;j<=r[i]-l[i]+1&&r[i]<tr[1];j++){
int p=find(1,1,k,r[i]+1,1);
f[p]=find(1,1,k,now,0);
now+=2;
if(now>r[i]) now=l[i]+(t^1);
}
}
for(int i=1,j=1;i<=k;i++)
if(f[i]) ans[i]=ans[f[i]];
else ans[i]=s[j++];
puts(ans+1);
}
int main(){
gets(s+1);
scanf("%d%d",&k,&n);
build(1,1,k);
for(int i=1;i<=n;i++)
scanf("%d%d",&l[i],&r[i]);
work();
return 0;
}