Codeforces Round #741 (Div. 2) D2. Two Hundred Twenty One (hard version) 前缀和 + 分段模型

传送门

文章目录

题意:

给你一个长度为 n n n的串, + + +代表 1 1 1 − - 代表 − 1 -1 1,让后有 q q q个询问,每次询问 [ l , r ] [l,r] [l,r]区间,将这段区间的数拿出来,设为 a [ 1 , r − l + 1 ] a[1,r-l+1] a[1,rl+1],算 a 1 − a 2 + a 3 − . . . = x a_1-a_2+a_3-...=x a1a2+a3...=x,问最少删去多少个数才能使得 x x x 0 0 0,输出删的数的位置。

n , q ≤ 3 e 5 n,q\le3e5 n,q3e5

思路:

首先考虑简单版本,就是不要求输出删的位置,只考虑删多少。

每次询问拿出来的区间,奇偶位置符号分别乘 1 , − 1 1,-1 1,1,不难发现我们将 − 1 , 1 -1,1 1,1倒过来也是没有影响的,也就是偶奇分别乘 1 , − 1 1,-1 1,1

那么既然拿出来的区间对位置奇偶没影响,那么我们考虑一开始就处理一个 a [ i ] a[i] a[i]表示第 i i i个位置的数,再维护个前缀和 s u m [ i ] sum[i] sum[i],我们现在能得到 [ l , r ] [l,r] [l,r]的和了,考虑如何计算答案。

( 1 ) (1) (1)如果当前区间和是 0 0 0,那么答案显然为 0 0 0

( 2 ) (2) (2)如果当前区间和为奇数,那么一定可以找到一个位置,去掉当前位置后区间和为 0 0 0

( 3 ) (3) (3)如果当前区间和为偶数,那么我们可以把 l l l的位置删掉,这样区间和一定变为奇数,让后再按照 ( 2 ) (2) (2)来找一个位置去掉即可。

所以如果是 e a s y easy easy版本的话,分以上三种讨论即可。

考虑如果要求输出位置怎么办。

对于 ( 1 ) (1) (1),直接输出 0 0 0即可。

对于 ( 2 ) (2) (2),由于其一定存在一个位置去掉后区间和为 0 0 0,考虑什么位置去掉是合法的呢?假设我们去掉的位置是 p o s pos pos,那么 [ l , p o s − 1 ] , [ p o s + 1 , r ] [l,pos-1],[pos+1,r] [l,pos1],[pos+1,r]两个区间的和原本是相同的,也就是 s u m [ l , p o s − 1 ] = s u m [ p o s + 1 , r ] sum_{[l,pos-1]}=sum_{[pos+1,r]} sum[l,pos1]=sum[pos+1,r],那么我们去掉 p o s pos pos位之后,前面的不变,后面的值全部取反,也就是 s u m [ l , p o s − 1 ] = − s u m [ p o s + 1 , r ] sum_{[l,pos-1]}=-sum_{[pos+1,r]} sum[l,pos1]=sum[pos+1,r],也就是说我们找到这样的一个合法位置即可。

考虑怎么找呢?显然可以二分来找,二分位置,有点小繁琐,我们考虑一种更加妙的方式。

我们给每个 s u m [ i ] + s u m [ i − 1 ] sum[i]+sum[i-1] sum[i]+sum[i1]开一个桶,存下来 i i i这个位置,考虑当前询问区间为 [ l , r ] [l,r] [l,r]。你那么我们找到 s u m [ l − 1 ] + s u m [ r ] sum[l-1]+sum[r] sum[l1]+sum[r]对应的桶里的某个位置 x x x,那么现在有 s u m [ r ] + s u m [ l − 1 ] = s u m [ x ] + s u m [ x − 1 ] sum[r]+sum[l-1]=sum[x]+sum[x-1] sum[r]+sum[l1]=sum[x]+sum[x1],移项得 s u m [ r ] − s u m [ x ] = s u m [ x − 1 ] − s u m [ l − 1 ] sum[r]-sum[x]=sum[x-1]-sum[l-1] sum[r]sum[x]=sum[x1]sum[l1],不难发现,如果我们将 x x x这个位置去掉后,那么前后两个区间的和就为 0 0 0了,所以我们只需要在 s u m [ i ] + s u m [ i − 1 ] sum[i]+sum[i-1] sum[i]+sum[i1]的桶内二分一个位置即可,由于保证一定有解,所以一定存在这样的一个位置。

对于 ( 3 ) (3) (3),删掉 l l l位置,将其变为奇数,让后进行 ( 2 ) (2) (2)即可。

// Problem: D1. Two Hundred Twenty One (easy version)
// Contest: Codeforces - Codeforces Round #741 (Div. 2)
// URL: https://codeforces.com/contest/1562/problem/D1
// Memory Limit: 512 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

//#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
//#pragma GCC optimize(2)
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<cmath>
#include<cctype>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<sstream>
#include<ctime>
#include<cstdlib>
#include<random>
#include<cassert>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define pb push_back
#define mk make_pair
#define Mid ((tr[u].l+tr[u].r)>>1)
#define Len(u) (tr[u].r-tr[u].l+1)
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define db puts("---")
using namespace std;

//void rd_cre() { freopen("d://dp//data.txt","w",stdout); srand(time(NULL)); }
//void rd_ac() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//AC.txt","w",stdout); }
//void rd_wa() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//WA.txt","w",stdout); }

typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;

const int N=2000010,mod=1e9+7,INF=0x3f3f3f3f;
const double eps=1e-6;

int n,q;
int a[N],sum[N];
vector<int>v[N];
char s[N];

int get(int l,int r) {
	int now=sum[l]+sum[r]+2*n;
	int pos=lower_bound(v[now].begin(),v[now].end(),l+1)-v[now].begin();
	return v[now][pos];
}

int main()
{
//	ios::sync_with_stdio(false);
//	cin.tie(0);
	
	int _; scanf("%d",&_);
	while(_--) {
		scanf("%d%d%s",&n,&q,s+1);
		for(int i=1;i<=n;i++) {
			if(s[i]=='-') {
				if(i%2==1) sum[i]=sum[i-1]-1;
				else sum[i]=sum[i-1]+1;
			}
			else {
				if(i%2==1) sum[i]=sum[i-1]+1;
				else sum[i]=sum[i-1]-1;
			}
		}
		for(int i=1;i<=n;i++) v[sum[i]+sum[i-1]+2*n].pb(i);
		while(q--) {
			int l,r; scanf("%d%d",&l,&r);
			int now=abs(sum[r]-sum[l-1]);
			if(now==0) puts("0");
			else if((r-l+1)%2==1) {	
				puts("1");
				printf("%d\n",get(l-1,r));
			} else {
				puts("2");
				printf("%d %d\n",l,get(l,r));
			}
		}
		for(int i=1;i<=n;i++) v[sum[i]+sum[i-1]+2*n].clear();
	}



	return 0;
}
/*
1
14 3
+--++---+++---  
1 14
6 12  ---+++- -1 1 -1 -1 1 -1 -1 
3 10

2
1 5
1
12
0


*/









  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值