hdu 2430 线段树维护下标

求一段连续的区间,使其在满足 和 对p取余不超过k的 前提 下 和最大 。

做法:

用线段树维护区间最小值,最小的下标,即最左边,因为加入sum[i]%p>k就要减去一个sum[id]   ,  sum[id]%p在【sum[i]%p-k,sum[i]%p】之间,

sum[i]-sum[id]的值就为以i结尾的满足条件的连续的一段数的最大值,所以id要尽可能小,所以就是求值在【sum[i]%p-k,sum[i]%p】之间的最左边的数的下标,即满足条件的数的下标最小值。

每次更新时把进过的结点的下标最小值(如果能更新)都更新一遍

View Code
#include<cstdio>
#include<cstring>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int inf = 1000000000;
const int maxn = 1000010;
__int64 sum[maxn];
int Min[maxn<<2];
int min(int a,int b){
return a<b?a:b;
}
void build(int l,int r,int rt){
Min[rt]=inf;
if(l==r) return ;
int m=(l+r)>>1;
build(lson);
build(rson);
}
int update(int num,int id,int l,int r,int rt){
if(l==r&&num==l){
if(id<Min[rt])
Min[rt]=id;
return Min[rt];
}
int m=(l+r)>>1;
int a;
if(num<=m) a=update(num,id,lson);
if(num>m) a=update(num,id,rson);
if(Min[rt]>a) Min[rt]=a;
return Min[rt];
}
int query(int L,int R,int l,int r,int rt){
if(L<=l&&r<=R){
return Min[rt];
}
int m=(l+r)>>1;
int ret=inf;
if(L<=m) ret=min(ret,query(L,R,lson));
if(R>m) ret=min(ret,query(L,R,rson));
return ret;
}
int main()
{
int t,ca=1,i,j,k,p,n;
scanf("%d",&t);
while(t--){
scanf("%d%d%d",&n,&p,&k);
sum[0]=0;
for(i=1;i<=n;i++){
scanf("%d",&j);
sum[i]=sum[i-1]+j;
}
build(0,p-1,1);
update(0,0,0,p-1,1);
int id=0;
int ans=-1;
for(i=1;i<=n;i++){
if(sum[i]%p>k) id=query(sum[i]%p-k,sum[i]%p,0,p-1,1);
else id=0;
if(id!=inf){
if((sum[i]-sum[id])/p>ans) ans=(sum[i]-sum[id])/p;
}
update(sum[i]%p,i,0,p-1,1);
}
if(ans==-1)
printf("Case %d: %d\n",ca++,-1);
else printf("Case %d: %d\n",ca++,ans);
}
return 0;
}



你可能感兴趣的:(HDU)