CF1562 D. Two Hundred Twenty One (easy&hard version) [数学+数形结合好题]

传送门:CF

[前题提要]:无.


先考虑 D 1 D1 D1.把玩一下题意就不难发现取了一根棒之后,后面的所有都是往前移一格,也就是后缀的贡献此时贡献乘上了一个 − 1 -1 1.
然后我们想一下我们取了一根棒之后对整体会发生什么.
不妨假设我们的区间是 [ l , r ] [l,r] [l,r],取的棒是 p o s pos pos位置,显然这根棒的贡献可以是 1 , − 1 1,-1 1,1(注意,此时指的是贡献,而不单单只是值).我们假设棒之前的贡献为 x x x,后缀贡献是 y y y.刚开始的贡献和为 z z z.不妨假设此时是最后一次拿棒.那么此时就有 x + 1 + y = z x+1+y=z x+1+y=z z − 1 − 2 ∗ y = 0 z-1-2*y=0 z12y=0或者 x − 1 + y = z x-1+y=z x1+y=z z + 1 − 2 ∗ y = 0 z+1-2*y=0 z+12y=0解一下上述两个方程组,就会发现我们的 x = y = z − 1 2 或者 x = y = z + 1 2 x=y=\frac{z-1}{2}或者x=y=\frac{z+1}{2} x=y=2z1或者x=y=2z+1但是是有限制条件的,前者需要保证取的贡献为 1 1 1,后者需要保证取的贡献为 − 1 -1 1.
诶,此时会发现如果我们最后的 z z z是奇数的话,会发现,我们总是能找到一个 x , y x,y x,y,因为此时他们的取值小于 z z z(这里不是非常严谨,但是先按下不表),并且每次贡献和的差不会超过1.但是这对吗?我们似乎还要保证后面那一位的贡献才对.
考虑对 z z z进行分类讨论:
假设此时的 z z z是正奇数,那么此时,我们不妨画出下列贡献和变化图像:
在这里插入图片描述
你会发现,如果我们找第一个等于 z − 1 2 + 1 \frac{z-1}{2}+1 2z1+1的点,我们总能保证该点的贡献为1,且前缀贡献为 z − 1 2 \frac{z-1}{2} 2z1,为什么呢,因为我们找的点是第一个到达此位置的点,说明之前没有任意一个点在这个点的上方,因为他们都是从小往大增加的,所以该点此时必然是最大值,所以必然为1.
需要格外注意的是,此时对于另外一种情况,也就是找第一个 z + 1 2 − 1 \frac{z+1}{2}-1 2z+11的点,此时我们就无法保证该情况的正确性了,因为第一个该值的点不可能使 − 1 -1 1,如果我们找第二个点,那么此时的维护方式就比价麻烦.

对于负数情况,我们同样可以得出,找一个等于 z + 1 2 − 1 \frac{z+1}{2}-1 2z+11的点必然满足情况,因为方法同上,此处就不在赘述了.

此时,我们就可以得出,对于初始贡献为奇数的情况,我们都可以通过修改一个点来将其改为0.那么对于偶数我们该怎么办呢.首先需要注意的是,我们最后的序列必然是一个偶数序列,因为对于一个序列来说,我们将其两个两个分组,会得到 11 , 10 , 01 , 00 11,10,01,00 11,10,01,00这四种贡献组合,贡献分别为 0 , 2 , − 2 , 0 0,2,-2,0 0,2,2,0,是偶数贡献的,所以如果是奇数,最后必然不可能变成0.所以对于偶数情况,我们的操作次数必然大于等于2.因此,我们大可以花一费将偶数改为奇数,然后奇数情况可以再花一费变成0(这个部分在上述仔细讨论过了),所以此时操作数为2.


下面是具体的代码部分( h a r d    v e r s i o n hard\;version hardversion):

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls (rt<<1)
#define rs (rt<<1|1)
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
//inline void print(__int128 x){
//	if(x<0) {putchar('-');x=-x;}
//	if(x>9) print(x/10);
//	putchar(x%10+'0');
//}
#define maxn 1000000
const double eps=1e-8;
#define	int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int sum[maxn];vector<int>v[maxn];int a[maxn];
int main() {
	cin.sync_with_stdio(false);cin.tie();cout.tie();
	int T;cin>>T;
	while(T--) {
		int n,q;cin>>n>>q;
		string s;cin>>s;s=" "+s;
		v[n].push_back(0);
		for(int i=1;i<=n;i++) {
			if(s[i]=='+') sum[i]=sum[i-1]+1*pow(-1,i-1),a[i]=1;
			else sum[i]=sum[i-1]-1*pow(-1,i-1),a[i]=-1;
			v[sum[i]+n].push_back(i);
		}
		for(int i=1;i<=q;i++) {
			int l,r;cin>>l>>r;
			int base=-1;
			if(l&1) base=1;
			if((r-l+1)&1) {
				cout<<1<<endl;
				int z=(sum[r]-sum[l-1])*base;
				int temp;
				if(z<0) temp=(z-1)/2;
				else temp=(z+1)/2;
				int num=sum[l-1]+base*temp;
				auto pos=lower_bound(v[num+n].begin(),v[num+n].end(),l);
				if(pos!=v[num+n].end()) {
					cout<<*pos<<endl;
				}
			}
			else {
				if(sum[r]-sum[l-1]==0) {
					cout<<0<<endl;
				}
				else {
					cout<<2<<endl;
					cout<<r<<" ";
					r--;
					int z=(sum[r]-sum[l-1])*base;
					int temp;
					if(z<0) temp=(z-1)/2;
					else temp=(z+1)/2;
					int num=sum[l-1]+base*temp;
					auto pos=lower_bound(v[num+n].begin(),v[num+n].end(),l);
					if(pos!=v[num+n].end()) {
						cout<<*pos<<endl;
					}
				}
			}
		}
		for(int i=0;i<=2*n;i++) v[i].clear();
	}
	return 0;
}
  • 16
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值