https://www.zybuluo.com/ysner/note/1237581
题面
给定一个长度为(n)的数列,以及(m)次询问。每次询问在模(p)意义下,([l,r])中的最小区间和。
- (40pts nleq200,mleq1000,pleq500)
- (100pts nleq5*10^5,mleq10^4,pleq500)
解析
或许是我看到的第(n)道卡不动暴力的题。(均因为不好造数据,类比[数字对][1])
(40pts)算法
枚举(l,r),用前缀和。
复杂度(O(mn^2))。
(90pts)算法
注意如果询问的序列长度大于(p),那么答案一定为(0)。
因为在极端情况下,长度为(p)的区间中每个前缀和各不相同,覆盖了模(p)的所有余数。
此时再加一个数,一定有前缀和相减得(0)。
特判即可。
(100pts)暴力算法
在上面特判的基础上,我们可以优化一下枚举过程。
我们从前往后枚举区间。对枚到位置的区间前缀和,再从大到小枚举数字,如果数字在前面出现过就(break),统计答案。
同时如果(ans=0)就(break)。
复杂度(O(mp^2)),但一点都不满,且难卡掉。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#define re register
#define il inline
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define fp(i,a,b) for(re int i=a;i<=b;i++)
#define fq(i,a,b) for(re int i=a;i>=b;i--)
using namespace std;
const int mod=1e9+7,N=5e5+100;
int n,m;
ll a[N],ans,res[N];
bool vis[505];
il ll gi()
{
re ll x=0,t=1;
re char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') t=-1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
return x*t;
}
int main()
{
freopen("seq.in","r",stdin);
freopen("seq.out","w",stdout);
n=gi();m=gi();
fp(i,1,n) a[i]=gi();
while(m--)
{
re int l=gi(),r=gi(),p=gi();ll ans=1e18;
if(r-l+1>p) {puts("0");continue;}
res[l-1]=0;
fp(i,1,p) vis[i]=0;vis[0]=1;
fp(i,l,r)
{
res[i]=(res[i-1]+a[i])%p;
fq(j,res[i],0) if(vis[j]) {ans=min(ans,res[i]-j);break;}
if(!ans) break;
vis[res[i]]=1;
}
printf("%lld
",ans);
}
fclose(stdin);
fclose(stdout);
return 0;
}
(100pts)正解算法
需用平衡树寻找最小差值。
我连平衡树都不怎么会,懒得写了
[1]: https://www.cnblogs.com/yanshannan/p/9060931.html