题解
签到题
求区间和为 (k) 的倍数的区间,我们可以转化为求左右两个端点,其前缀和相等
对于区间最大值,我们可以把其转化为一个值,它能向左,向右扩展的最远边界,一个单调栈即可
我们设一个值 (i),它能扩展的左右边界分别为 (l_i,r_i) 那么我们将 (l_i~r_i) 分为两部分,(l_i~i),(i~r_i)
枚举较小的那一段(可以证明总复杂度为 (mathcal O(nlogn)) ),在另一段寻找前缀和等于此前缀和加 (num_i) 的个数,用一颗动态开点线段树即可
注意:此种做法边界要卡得很细致
Code:
#include<bits/stdc++.h>
#define ri register signed
#define p(i) ++i
using namespace std;
namespace IO{
char buf[1<<21],*p1=buf,*p2=buf;
#define gc() p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++
template<typename T>inline void read(T &x) {
ri f=1;x=0;register char ch=gc();
while(ch<'0'||ch>'9') {if (ch=='-') f=0;ch=gc();}
while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
x=f?x:-x;
}
}
using IO::read;
namespace nanfeng{
#define cmax(x,y) ((x)>(y)?(x):(y))
#define cmin(x,y) ((x)>(y)?(y):(x))
#define FI FILE *IN
#define FO FILE *OUT
static const int N=3e5+7,K=1e6+1;
int num[N],r[N],l[N],st[N],sum[N],nm[N],ans,tp,n,k;
struct Segmenttree{
#define ls(x) T[x].l
#define rs(x) T[x].r
#define up(x) T[x].nm=T[ls(x)].nm+T[rs(x)].nm
struct seg{int l,r,nm;}T[K*20];
int rt[K],tot;
void update(int &x,int p,int l,int r) {
if (!x) x=p(tot);
if (l==r) {p(T[x].nm);return;}
int mid(l+r>>1);
if (p<=mid) update(ls(x),p,l,mid);
else update(rs(x),p,mid+1,r);
up(x);
}
int query(int x,int l,int r,int lt,int rt) {
if (!x) return 0;
if (l<=lt&&rt<=r) return T[x].nm;
int mid(lt+rt>>1),res(0);
if (l<=mid) res+=query(ls(x),l,r,lt,mid);
if (r>mid) res+=query(rs(x),l,r,mid+1,rt);
return res;
}
}T;
inline int main() {
// FI=freopen("nanfeng.in","r",stdin);
// FO=freopen("nanfeng.out","w",stdout);
read(n),read(k);
for (ri i(1);i<=n;p(i)) {
read(nm[i]);num[i]=nm[i]%k;
sum[i]=(sum[i-1]+num[i])%k;
T.update(T.rt[sum[i]],i,1,n);
while(tp&&nm[st[tp]]<=nm[i]) r[st[tp--]]=i-1;
l[i]=st[tp]+1;
st[p(tp)]=i;
}
while(tp) r[st[tp--]]=n;
for (ri i(1);i<=n;p(i)) {
if (i==l[i]&&i==r[i]) continue;
if (i-l[i]<=r[i]-i) {
for (ri j(l[i]);j<i;p(j))
ans+=T.query(T.rt[(sum[j-1]+num[i])%k],i,r[i],1,n);
if (i<r[i]) ans+=T.query(T.rt[sum[i]],i+1,r[i],1,n);
} else {
for (ri j(i+1);j<=r[i];p(j))
if (l[i]-1) ans+=T.query(T.rt[(sum[j]-num[i]+k)%k],l[i]-1,i-1,1,n);
else {
ri tmp=(sum[j]-num[i]+k)%k;
ans+=T.query(T.rt[tmp],l[i],i-1,1,n);
if (!tmp) p(ans);
}
if (l[i]-1) ans+=T.query(T.rt[sum[i-1]],l[i]-1,i-2,1,n);
else {
if (i-2) ans+=T.query(T.rt[sum[i-1]],1,i-2,1,n);
if (!sum[i-1]) p(ans);
}
}
}
printf("%d
",ans);
return 0;
}
}
int main() {return nanfeng::main();}
官方题解做法,为 (mathcal O(nlogn)) 复杂度较优,但其实没什么太大区别