Description
给出一个数列a1,a2,。。。,an和K,P。 设Si,j = ai + ai+1::: + aj Answer = min{Si,j mod P|Si,j mod P>=K},其中i<=j,{Si,j mod P|Si,j mod P>=K}非空。
Input
第一行一个正整数n,K,P。 第二行n个整数,表示一个一个数列a1; a2:::; an
Output
在第一行输出Answer。
Sample Input
7 2 17 12 13 15 11 16 26 11
Sample Output
2
Data Constraint
数据范围 在100%的数据中,1<=n<=100000,1<=K,P,ai<=10^8; i =1,2,,,n
Solution
题目大意就是让你求出最小的一段区间和模p大于等于k的值。设S[i]表示ai的前缀和,则问题简化为ans=min{ ( S[ j ] - S[ i ] ) % p } ( i < j && ans>=k )。大致思路如下:对于每一个S[ j ],我们都要找到一个S[ i ],满足( S[ j ]-S[ i ] ) % p>=k 最小,再用这个最小值更新即可。如何最小?不妨先来考虑如何使这个值>=k,因为我们一开始的S[ i ]都是已经 %p了的,所以就要分类讨论,若S[ i ]<S[ j ],则只需要找所有小于等于( S[ j ] - k )的数中最大的,即为S[ i ]。若S[ i ]>S[ j ],则原式=( S[ j ] + p - S[ i ] ) % p,所以我们就要找到所有小于等于( S[ j ] + p - k)的数中最大的那个,如何找?我们可以每做完一个数就将它加入权值线段树中,然后每次查询前面的数即可。
Code
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAX 100000000
#define N 100010
using namespace std;
int n,k,p,f[N*30],ls[N*30],rs[N*30],tot=1;
int a[N],s[N],ans=1<<30,x;
void insert(int x,int l,int r,int k){
if(l==r){f[x]=l;return;}
int mid=(l+r)>>1;
if(k<=mid){
if(!ls[x]) ls[x]=++tot;
insert(ls[x],l,mid,k);
}
else{
if(!rs[x]) rs[x]=++tot;
insert(rs[x],mid+1,r,k);
}
f[x]=min(f[ls[x]],f[rs[x]]);
}
int findp(int x,int l,int r){
if(l==r) return l;
int m=l+r>>1;
if(rs[x]) return findp(rs[x],m+1,r);
if(ls[x]) return findp(ls[x],l,m);
}
int find(int x,int l,int r,int k){
if(k>r){
if(f[x]) return findp(x,l,r);
return 0;
}
int m=(l+r>>1),re;
if(k>m&&rs[x]) return find(rs[x],m+1,r,k);
if(ls[x]) return find(ls[x],l,m,k);
}
int main(){
scanf("%d%d%d",&n,&k,&p);
for(int i=1;i<=n;i++){
scanf("%d",&x);
s[i]=(s[i-1]+x)%p;
}
memset(f,127,sizeof(f));
for(int i=1;i<=n;i++){
x=find(1,0,p,s[i]-k);
if(x>0&&s[i]>x) ans=min(ans,s[i]-x);
x=find(1,0,p,s[i]+p-k);
if(x>0&&s[i]+p-k>x) ans=min(ans,s[i]+p-x);
insert(1,0,p,s[i]);
}
printf("%d\n",ans);
return 0;
}
作者:zsjzliziyang
QQ:1634151125
转载及修改请注明
本文地址:https://blog.csdn.net/zsjzliziyang/article/details/87473509