CodeForces 374 D. Inna and Sequence

题意:给定十万个数a[ m ],然后有十万个操作,每次给现有序列加一个字符(0或1),或者删掉已有序列中,第 a[0] 个,第a[1]个,...,第a[m]个。

序列最多有10万个1或0.   删除操作最多进行10万次。

于是用线段树来储存。每个节点,存这个点还剩下多少个点没有删除,这样就可以删除指定的第k 个数。

给序列加元素最多进行n次,每次复杂度 O(log2(n)) ,最多 O(n*log2(n));

删除元素最多进行 n 次,每次复杂度 O(log2(n)) ,最多 O(n*log2(n));

所以总复杂度O(n*log2(n));

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#define maxn 1000010
using namespace std;
int N,n,m,Now,op;
int a[maxn];
int index[maxn<<2];
int value[maxn];
void ST_Init(){
	N=1;while(N <n+2) N <<=1;
	memset(index,0,sizeof(index));
}
void PushUp(int rt){
	index[rt]=index[rt<<1]+index[rt<<1|1];
}
void Set(int X,int C,int l,int r,int rt){//将X位置设置为C
	if(l==r){
		value[l]=C;
		index[rt]=1;
		++Now;
		return;
	}
	int m=(l+r)>>1;
	if(X <=m) Set(X,C,l,m,rt<<1);
	else Set(X,C,m+1,r,rt<<1|1);
	PushUp(rt);
}
void Del(int X,int l,int r,int rt){//删除第X个元素
	if(l==r){
		index[rt]=0;
		return;
	}
	int m=(l+r)>>1;
	if(X <= index[rt<<1]) Del(X,l,m,rt<<1);
	else Del(X-index[rt<<1],m+1,r,rt<<1|1);
	PushUp(rt);
}
void show(){//显示
	for(int i=0;i<Now;++i){
		if(index[N+i]){
			printf("%d",value[i+1]);
		}
	}
	cout<<endl;
}
int main(void)
{
	 
	while(~scanf("%d%d",&n,&m)){
		Now=1;ST_Init();
		for(int i=0;i<m;++i) scanf("%d",&a[i]);
		for(int i=0;i<n;++i) {
			scanf("%d",&op);
			if(~op){
				Set(Now,op,1,N,1);
			}
			else{
				for(int j=0;j<m&&a[j]-j<=index[1];j++){//<span style="font-family: Arial, Helvetica, sans-serif;">注意,这里是删掉第a[ j ]</span><span style="font-family: Arial, Helvetica, sans-serif;">个元素,</span>
					Del(a[j]-j,1,N,1);//因为已经删掉了j个元素了,本次操作前的第a[j]个是现在的第 <span style="font-family: Arial, Helvetica, sans-serif;">a[ j ] - j 个</span><span style="font-family: Arial, Helvetica, sans-serif;">。</span>
				}
			}
		}
		if(index[1]){
			show();
		}
		else{
			printf("Poor stack!\n");
		}
	}
return 0;
}













CodeForces - 616D是一个关于找到一个序列中最长的第k好子段的起始位置和结束位置的问题。给定一个长度为n的序列和一个整数k,需要找到一个子段,该子段中不超过k个不同的数字。题目要求输出这个序列最长的第k好子段的起始位置和终止位置。 解决这个问题的方法有两种。第一种方法是使用尺取算法,通过维护一个滑动窗口来记录\[l,r\]中不同数的个数。每次如果这个数小于k,就将r向右移动一位;如果已经大于k,则将l向右移动一位,直到个数不大于k。每次更新完r之后,判断r-l+1是否比已有答案更优来更新答案。这种方法的时间复杂度为O(n)。 第二种方法是使用枚举r和双指针的方法。通过维护一个最小的l,满足\[l,r\]最多只有k种数。使用一个map来判断数的种类。遍历序列,如果当前数字在map中不存在,则将种类数sum加一;如果sum大于k,则将l向右移动一位,直到sum不大于k。每次更新完r之后,判断i-l+1是否大于等于y-x+1来更新答案。这种方法的时间复杂度为O(n)。 以上是两种解决CodeForces - 616D问题的方法。具体的代码实现可以参考引用\[1\]和引用\[2\]中的代码。 #### 引用[.reference_title] - *1* [CodeForces 616 D. Longest k-Good Segment(尺取)](https://blog.csdn.net/V5ZSQ/article/details/50750827)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Codeforces616 D. Longest k-Good Segment(双指针+map)](https://blog.csdn.net/weixin_44178736/article/details/114328999)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值