题目描述
给定一个长度为$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++
原文地址:https://www.cnblogs.com/wzc521/p/11494378.html
时间: 2024-10-09 15:40:59