CF217E Alien DNA

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;
}
原文地址:https://www.cnblogs.com/jasony/p/13587625.html