[CSP-S模拟测试]:简单的区间(分治)

Posted wzc521

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[CSP-S模拟测试]:简单的区间(分治)相关的知识,希望对你有一定的参考价值。

题目描述

给定一个长度为$n$的序列$a$以及常数$k$,序列从$1$开始编号。

$$f(l,t)=\sum \limits_i=l^ra_i-\max \limits_i=l^r\a_i\$$
求合法的正整数对$(l,r)$的数量,满足$1\leqslant l<r\leqslant n$,且$k|f(l,r)$。


输入格式

第一行两个正整数$n$和$k$。
第二行包含$n$个正整数,第$i$个正整数表示$a_i$。


输出格式

一行一个正整数,表示答案。


样例

样例输入1:

4 3
1 2 3 4

样例输出1:

3

样例输入2:

4 2
4 4 7 4

样例输出2:

6


数据范围与提示

对于$30\%$的数据,$n\leqslant 3,000$;
对于另外$20\%$的数据,数列$a$为随机生成;
对于$100\%$的数据,$1\leqslant n\leqslant 3\times 10^5,1\leqslant k\leqslant 10^6,1\leqslant a_i\leqslant 10^9$。


题解

考虑分治,但是我的打法跟正解不太一样。

$solve(l,r)$代表左右端点都在$[l,r]$之间的合法区间数量。

显然我们肯定不能依次枚举每个端点,这样的时间复杂度还是$\Theta(n^2)$的,所以我们考虑优化。

我们可以指枚举一侧,然后在处理另一侧的时候计算这一侧的答案即可。

时间复杂度:$\Theta(n\log n)$。

期望的分:$100$分。

实际的分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
int n,k;
int a[300001];
int s[300001];
int maxn[300001],pos[300001];
int t1[10000001],t2[10000001];
long long ans;
void wzc(int l,int r)

	if(l==r)return;
	int mid=(l+r)>>1;
	int flag1=1,flag2=mid+1,mx=0;
	s[0]=s[mid]=maxn[0]=0;
	for(int i=mid+1;i<=r;i++)
	
		if(a[i]>a[maxn[maxn[0]]])maxn[++maxn[0]]=i;
		s[i]=(s[i-1]+a[i])%k;
		t1[(s[i]-a[maxn[maxn[0]]]%k+k)%k]++;
		pos[i]=maxn[maxn[0]];
	
	maxn[maxn[0]+1]=r+1;
	for(int i=mid;i>=l;i--)
	
		s[0]=(s[0]+a[i])%k;
		mx=max(mx,a[i]);
		while(flag1<=maxn[0]&&a[maxn[flag1]]<=mx)flag1++;
		while(flag2<maxn[flag1])
		
			t1[(s[flag2]-a[pos[flag2]]%k+k)%k]--;
			t2[s[flag2++]]++;
		
		if(flag1<=maxn[0])ans+=t1[(k-s[0])%k];
		ans+=t2[(mx%k-s[0]+k)%k];
	
	for(int i=mid+1;i<flag2;i++)t2[s[i]]--;
	for(int i=flag2;i<=r;i++)t1[(s[i]-a[pos[i]]%k+k)%k]--;
	wzc(l,mid);
	wzc(mid+1,r);

int main()

	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	wzc(1,n);
	printf("%lld",ans);
	return 0;


rp++

以上是关于[CSP-S模拟测试]:简单的区间(分治)的主要内容,如果未能解决你的问题,请参考以下文章

csp-s模拟测试58「Divisors」·「Market」·「Dash Speed」?

csp-s模拟测试94

[CSP-S模拟测试74]题解

[CSP-S模拟测试63]题解

csp-s模拟测试52平均数,序列题解

2019.9.19 csp-s模拟测试47 反思总结