1306. Sum

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值