题目大意
有一个长度为n的数组a,现在要找一个长度至少为2的子段,求出这一子段的和,然后减去最大值,然后对k取余结果为0。
问这样的子段有多少个。
数据范围
1 ≤ n ≤ 300 000, 1 ≤ k ≤ 1 000 000, 1 ≤ ai ≤ 109
分析
首先有一个思路:枚举每个最大值,然后确定出以它为最大值的区间,能向左(或右)扩展到哪里,这个可以打两次单调栈解决。
剩下就是统计答案了。
首先设si=∑ij=1a[j]假设最大值为am,区间能向左(右)扩展到l,r。如果一个以am为最大值的区间[i,j](l≤i≤m,m≤j≤r)能够满足条件,当且仅当满足:(s[j]-s[i-1]-a[m])%k=0
统计答案
考虑枚举上述区间的左(或右)边界,然后就是插入一个形如(l,r,x)的询问,询问有多少个si (i∈[l,r]且si=x)。假设有q个询问,解决的时间复杂度为O(q+n)。
如果对于一个m,以am为最大值可以扩展到l,r,那么相当于把区间[l,r]切成两部分,然后如果左半部分比右半部分小,就枚举左边界,否则枚举右边界,那么q是nlogn的。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define fi first
#define se second
using namespace std;
const int maxn=300005,maxm=1000005;
typedef long long LL;
typedef pair<int,int> PII;
int n,m,a[maxn],s[maxn],l[maxn],r[maxn],lt[maxn],rt[maxn],top,st[maxn],rank[maxn],data[maxn],tot,cnt[maxm];
vector <int> pl[maxn],mi[maxn];
LL ans;
PII A[maxn];
bool cmp(PII a,PII b)
{
return a.fi<b.fi;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
s[i]=(s[i-1]+a[i])%m;
A[i].fi=a[i]; A[i].se=i; a[i]%=m;
}
sort(A+1,A+n+1,cmp);
for (int i=1;i<=n;i++) rank[A[i].se]=i;
st[top=1]=1;
for (int i=2;i<=n;i++)
{
if (rank[st[top]]<rank[i])
{
for (;top && rank[st[top]]<rank[i];top--);
lt[i]=st[top+1];
}
st[++top]=i;
}
st[top=1]=n;
for (int i=n-1;i;i--)
{
if (rank[st[top]]<rank[i])
{
for (;top && rank[st[top]]<rank[i];top--);
rt[i]=st[top+1];
}
st[++top]=i;
}
data[tot=1]=A[n].se;
r[data[1]]=n+1;
for (int i=1;i<=tot;i++)
{
int x=data[i];
if (x-l[x]<=r[x]-x)
{
for (int j=l[x];j<x;j++)
{
mi[x-1].push_back((s[j]+a[x])%m);
pl[r[x]-1].push_back((s[j]+a[x])%m);
}
}else
{
for (int j=x;j<r[x];j++)
{
if (l[x]) mi[l[x]-1].push_back((s[j]+m-a[x])%m);
pl[x-1].push_back((s[j]+m-a[x])%m);
}
}
if (lt[x])
{
l[lt[x]]=l[x]; r[lt[x]]=x; data[++tot]=lt[x];
}
if (rt[x])
{
l[rt[x]]=x; r[rt[x]]=r[x]; data[++tot]=rt[x];
}
}
ans=0;
for (int i=0;i<=n;i++)
{
cnt[s[i]]++;
vector <int> ::iterator it;
for (it=mi[i].begin();it!=mi[i].end();it++) ans-=cnt[*it];
for (it=pl[i].begin();it!=pl[i].end();it++) ans+=cnt[*it];
}
printf("%I64d\n",ans-n);
return 0;
}