codeforces 217E 【Alien DNA】

倒序考虑每一个操作,对于一个操作$[l, r]$,他产生的影响区间将是$[r+1,r + r + l - 1]$,如果$r+l-1>K$的话,$K$之后的区间我们是不关心的。

暴力扫描这个区间 然后对于每一个位置,维护一个类似于并查集的东西。

扫到位置$i$,定义数组$f$ 表示$i$这个位置上的字符是由$f_i$这个位置上的字符填充得到的。然后删除$i$。

这个东西具体怎么搞?我们可以弄一个长度为K,初始化为1的序列。获得第$i$个位置就是这个序列上第$i$个1,然后删除这个位置上的字符,就是把序列上该位置的1变成0。维护这个序列的话树状数组+二分或者树状数组+倍增花式搞,我选择后者,毕竟前者复杂度多一个$log$。不会的话可以看lyd那本书的P203(第一版),或者做一下poj 2182.

当然线段树维护也是可以的啦~

所以我们就得到如下算法:

倒序扫描每一个操作,维护一个变量$now$表示我们关心的区间末尾,对于每次操作的区间$[l,r]$,用一个变量$x$扫描$[r+1,r+l-1]$所要填充的字符的位置。每填充一个数$now$就减一,如果$r>now$就直接停止。因为$now$的缘故,最多填充次数为$K$。每次填充时,设$tmp$为01序列中第$r+1$个1,那么将$tmp$位置上的数减一,然后更新$f_{tmp}$为第$x$个1的位置

然后从左往右求解,如果$f_i$存在,那么$ans_i=ans_{f_i}$,不然$ans_i=s_{j+1},j++$ 由于$f_i$必然比$i$小 所以这样做一定是正确的

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const int N = 3e6 + 10;
char s[N], ans[N];
int n, K, len, f[N], c[N];
struct OPT{ int l, r;}a[5050];
int lowbit(int x){ return x & -x;}
void add(int x, int val){
	for(int i = x; i <= K; i += lowbit(i))	  c[i] += val;
}
int get(int x){
	int l = log2(K), sum = 0, ans = 0;
	for(int i = l; i >= 0; i--){
		int k = (1 << i);
		if(ans + k <= K && sum + c[ans + k] < x)
		  sum += c[ans + k], ans += k;
	}
	return ans + 1;
}
int main(){
	scanf("%s", s + 1);
	scanf("%d%d", &K, &n);
	for(int i = 1; i <= K; i++) c[i] = i & -i; //初始化树状数组为1
	for(int i = 1; i <= n; i++) scanf("%d%d", &a[i].l, &a[i].r);
	for(int now = K, i = n; i; i--)
	  for(int x = a[i].l + 1, j = 1; a[i].r < now && j <= a[i].r - a[i].l + 1; x += 2, j++, now--){
		  if(x > a[i].r) x = a[i].l;
		  int tmp = get(a[i].r + 1); add(tmp, -1), f[tmp] = get(x);
	  }
	for(int i = 1, j = 0; i <= K; i++)
	  ans[i] = f[i] ? ans[f[i]] : s[++j];
	puts(ans + 1);
	return 0;
}

  

猜你喜欢

转载自www.cnblogs.com/Rorshach/p/9846884.html
DNA