codeforces 217E 逆向思维

链接:http://codeforces.com/problemset/problem/217/E

题意:给你一个字符串,长度小于等于300W,再给你两个数,n,k接下来是n个操作,每个操作是对一段区间操作,将整段区间取出来,偶数位的字符放前面,奇数位的字符放在后面,然后插入这个区间的后面

比如  1 11   s1s2... s11, 将 s2s4s6s8s10s1s3s5s7s9s11插入到原字符串s11的的后面。




最后需要输出前k个字符。




一看到这道题目就有种神题的感觉,木有想法啊,只得求助题解http://codeforces.com/blog/entry/5285

然后就差不多有思路了

可以想到最后一次操作的区间不会被其他操作影响,所以,最后一次操作后,相应的改变的位置已经定了,不会再改变,所以我们逆着做,先用Ln Rn来对原字符串进行操作,

如果插入的位置已经超过了k,就不需要维护了,因为我们只需要维护前k个字符,在对当前L R进行操作之前,已经有很多个位置被占据了,而且这些被占据的位置已经是定死了,而本来需要插入i位置的某段复制后的区间现在肯定都往后移动了,往后移动的长度就是已经插入的字符的数量,所以,我们只需用一个线段树记录一下当前还有多少个空位即可,如果R > = 空位数,就不需要再继续插入了,因为已经超出k的范围了。插入的字符是什么字符的话,我们可以记录下当前插入位置的字符是从哪里复制来的,这个结合代码就能理解。

这道题的思维复杂度实在有点高,这样讲可能还不是很清楚,再附上一张截图以便理解。


如图所示,三段红色的区间分别是三段蓝红色波浪线所复制过来的区间,而且红色的区间已经定了,不会再改变,其实现在就相当于要操作的区间被一段段的"已经确定"的区间隔开了,然后用线段树转换成相对位置即可,比如本来题目要复制1~(x1+x2),但现在就变成了如图隔开的两段,插入的位置也要相应的后移了,现在再正向想想就懂了,假如已经将1~(x1+x2)的复制并插入好了,然后现在又要将1~x1复制一份,那么原来插入的那段区间肯定是要后移了,而且当插入的位置大于空位的个数时,就不用继续插入了

一个逆向思维,完美的解决了这道题目,wonderful!!!

#include <map>
#include <set>
#include <list>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <bitset>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std ;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn = 3000010;
int sum[maxn<<2];
void build(int l,int r,int rt) {
	sum[rt] = r-l+1;
	if(l==r) return ;
	int m=l+r>>1;
	build(lson);
	build(rson);
}
int update(int k,int l,int r,int rt) {
	sum[rt]--;
	if(l==r) return l;
	int m=l+r>>1;
	if(sum[rt<<1]>=k) return update(k,lson);
	else return update(k-sum[rt<<1],rson);
}
int find(int k,int l,int r,int rt) {
	if(l==r) return l;
	int m=l+r>>1;
    if(sum[rt<<1]>=k) return find(k,lson);
	else return find(k-sum[rt<<1],rson);
}
int x[5010],y[5010];
int fa[maxn];
char s[maxn];
char ans[maxn];
int main(){
	int m,n;
	scanf("%s",s+1);
	scanf("%d%d",&m,&n);
	for(int i=1;i<=n;i++) scanf("%d%d",&x[i],&y[i]);
    build(1,m,1);
	for(int i=n;i>=1;i--) {
		if(y[i]<sum[1]) {
			int now = x[i]+1;
			if(now>y[i]) now=x[i];
			int len=y[i]-x[i]+1;
			for(int j=1;j<=len && y[i] < sum[1];j++) {
				int k=update(y[i]+1,1,m,1);  
				fa[k]=find(now,1,m,1);
			//	printf("%d %d\n",k,fa[k]);
				now+=2;
				if(now > y[i])  now = x[i];
			}
		}
	}
	for(int i=1,j=1;i<=m;i++)
		if(fa[i]) ans[i]=ans[fa[i]];
		else ans[i]=s[j++];
		ans[m+1]=0;
		puts(ans+1);
	return 0;
}


你可能感兴趣的:(Build)