奇洛金卡达 father.cpp

11 篇文章 0 订阅
6 篇文章 0 订阅

(我还能怎样,能怎样,最后还不是像父亲一样把你原谅 ——鲁迅)

【简化题意】

给一个长度小于k的字符串和n个操作。
保证操作后字符串长度大于k,求最后的字符串中前k个字符。
操作规则如下:
给定一个(伪)区间[l,r],将区间中编号为奇数的字符形成一个新字符串,编号为偶数的字符形成一个字符串,将两个字符串拼接起来并放在编号为r字符的后边。
n<=5000,k<=3e6


【分析】

传统的思路上,不难发现我们只需要关心前k个字符即可。不需要多余的部分,然后就开心地水到了70分。
观察发现,超时的原因是因为我们产生了很多冗余的字符串,而这些字符串最终对答案并没有什么影响,产生了大量编号大于k的垃圾。
现在我们要避免产生垃圾,可以倒着往前做,求解密后的字符串第k位,和之前第几位是相同的,最终追溯到密文的那几位。再快速还原。先不求出具体的值,而是求出的问题中的拓扑关系。最终利用这个拓扑关系,快速还原出正解。

显然倒着做之后,确认过拓扑关系的点(也就是新产生的点)不会出现在上一步的字符串当中,所以要删除(或忽略),但是删除之后要重新拼接各个字符串并重新标号非常费时,所以我们考虑利用线段树来统计“在第i到第k位时有多少个数还未删除”,显然未删除的数就是当前状态下的编号。

【code】
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=3e6+100;
struct Tree{
	int l,r,val;
}t[maxn<<2];
int l[maxn],r[maxn],qian[maxn];
char s[maxn],ans[maxn];
int n,k,p,q;
inline void read(int &x){
	x=0;int fl=1;char tmp=getchar();
	while(tmp<'0'||tmp>'9'){if(tmp=='-')fl=-fl;tmp=getchar();}
	while(tmp>='0'&&tmp<='9') x=(x<<1)+(x<<3)+tmp-'0',tmp=getchar();
	x=x*fl;
}
void build(int x,int l,int r){
	t[x].l=l,t[x].r=r,t[x].val=r-l+1;
	if(l==r) return ;
	int mid=l+r>>1;
	build(x<<1,l,mid);
	build(x<<1|1,mid+1,r);
}
int change(int x,int l,int r,int y,int z){
	int mid;
	while(l<r){
		t[x].val-=z;
		mid=l+r>>1;
		if(t[x<<1].val<y)l=mid+1,y-=t[x<<1].val,x=(x<<1)+1;else r=mid,x=x<<1;
	}
	t[x].val-=z;
	return l;
}
int main(){
	freopen("father.in","r",stdin);
	freopen("father.out","w",stdout);
	scanf("%s",s+1);
	scanf("%d%d",&k,&n);
	for(int i=1;i<=n;i++)
		read(l[i]),read(r[i]);
	build(1,1,k);
	for(int j=n;j>=1;j--){
		if(r[j]>=t[1].val) continue;
		if(l[j]&1) p=l[j];
		else if(l[j]+1<=r[j]) p=l[j]+1;
		else p=l[j];
		for(int i=1;i<=r[j]-l[j]+1;i++){
			if(r[j]>=t[1].val) continue;
			q=change(1,1,k,r[j]+1,1);
			qian[q]=change(1,1,k,p,0);
			p+=2;
			if(p>r[j]){
				if(l[j]%2)p=l[j]+1;else p=l[j];
			}
		}
	}
	p=0;
	for(int i=1;i<=k;i++){
		if(qian[i])ans[i]=ans[qian[i]];else ans[i]=s[++p];
		putchar(ans[i]);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值