Description
题意给出n个谷堆,每个谷堆有wi个谷子
题
意
给
出
n
个
谷
堆
,
每
个
谷
堆
有
w
i
个
谷
子
现在要在n个谷堆中找出一个连续的区间[i,j]使得区间的和sumij
现
在
要
在
n
个
谷
堆
中
找
出
一
个
连
续
的
区
间
[
i
,
j
]
使
得
区
间
的
和
s
u
m
i
j
满足max{sumijp}(1≤i≤n;1≤j≤n;sumij%p≤k)
满
足
m
a
x
{
s
u
m
i
j
p
}
(
1
≤
i
≤
n
;
1
≤
j
≤
n
;
s
u
m
i
j
%
p
≤
k
)
Input
T≤20
T
≤
20
0<N,P<1000001;0≤k<P
0
<
N
,
P
<
1000001
;
0
≤
k
<
P
0≤wi<32768
0
≤
w
i
<
32768
Output
对于每个样例输出Case #X:Y
对
于
每
个
样
例
输
出
C
a
s
e
#
X
:
Y
X是样例编号
X
是
样
例
编
号
Y是max{sumijp}
Y
是
m
a
x
{
s
u
m
i
j
p
}
Solution
Solution1 SegmentTree
S
o
l
u
t
i
o
n
1
S
e
g
m
e
n
t
T
r
e
e
利用线段树是很好想这一题的。
首先预处理出前缀和,通过枚举区间的右端点j。
首
先
预
处
理
出
前
缀
和
,
通
过
枚
举
区
间
的
右
端
点
j
。
然后就是找到一个离j最远的i使得满足(sum[j]−sum[i])%P≤k。
然
后
就
是
找
到
一
个
离
j
最
远
的
i
使
得
满
足
(
s
u
m
[
j
]
−
s
u
m
[
i
]
)
%
P
≤
k
。
因为最远肯定是最大的。
找的最远的i的话利用权值线段树,权值线段树中下标是[0,P),值是数组的下标(如i,j等)。
找
的
最
远
的
i
的
话
利
用
权
值
线
段
树
,
权
值
线
段
树
中
下
标
是
[
0
,
P
)
,
值
是
数
组
的
下
标
(
如
i
,
j
等
)
。
设我们枚举了右端点j,x=sum[j]%P(sum[j]是区间[1,j]的和)。
设
我
们
枚
举
了
右
端
点
j
,
x
=
s
u
m
[
j
]
%
P
(
s
u
m
[
j
]
是
区
间
[
1
,
j
]
的
和
)
。
那么我们需要y=sum[i]满足以下条件
那
么
我
们
需
要
y
=
s
u
m
[
i
]
满
足
以
下
条
件
if(x>=k) x−K<=y<=x
i
f
(
x
>=
k
)
x
−
K
<=
y
<=
x
利用线段树去查询区间[x−K,x]的最小值i(最小下标)。
利
用
线
段
树
去
查
询
区
间
[
x
−
K
,
x
]
的
最
小
值
i
(
最
小
下
标
)
。
else 则答案就是arr[j]−arr[0]
e
l
s
e
则
答
案
就
是
a
r
r
[
j
]
−
a
r
r
[
0
]
因为对于当前x已经满足了x=(sum[j]−sum[0]%P)≤K,最远的就是0。
因
为
对
于
当
前
x
已
经
满
足
了
x
=
(
s
u
m
[
j
]
−
s
u
m
[
0
]
%
P
)
≤
K
,
最
远
的
就
是
0
。
Code1 C o d e 1
/*
* 题意给出n个谷堆,每个谷堆有wi个谷子,
* 现在仅仅允许取出一个区间[i-j]使得max((sum[j]-sum[i-1])/p) && (sum[j]-sum[i-1])%p <= k;
* 首先预处理出前缀和,通过枚举区间的右端点j,然后就是找到一个离j最远的i使得满足(sum[j]-sum[i-1]) % p <= k
* 因为最远肯定是最大的。
* 找的最远的i的话利用权值线段树,权值线段树中下标是[0,p),值是数组的下标(如 i,j等)
* 设我们枚举了右端点j,x = sum[j] % p (sum[j] 是 前缀和) 那么我们需要 y = sum[i] 满足以下条件
* 如果 x >= k x-k<=y<=x
* 如果 x < k 则答案就是arr[i] - arr[0] 因为对于这个i已经满足了,最远的就是0
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6+10;
const int inf = 0x3f3f3f3f;
struct SegTree {
int Min[maxn<<2];
void push_up(int rt) {
Min[rt] = min(Min[rt<<1],Min[rt<<1|1]);
}
void build(int l,int r,int rt) {
if(l == r) {
Min[rt] = inf;
return;
}
int mid = (l + r) >> 1;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
push_up(rt);
}
void update(int pos,int val,int l,int r,int rt) {
if(l == r) {
if(val < Min[rt]) Min[rt] = val;
return;
}
int mid = (l + r) >> 1;
if(pos <= mid) update(pos,val,l,mid,rt<<1);
else update(pos,val,mid+1,r,rt<<1|1);
push_up(rt);
}
int query(int ql,int qr,int l,int r,int rt) {
if(ql == l && qr == r) return Min[rt];
int mid = (l + r)>>1;
if(qr <= mid) return query(ql,qr,l,mid,rt<<1);
if(ql > mid) return query(ql,qr,mid+1,r,rt<<1|1);
return min(query(ql,mid,l,mid,rt<<1),query(mid+1,qr,mid+1,r,rt<<1|1));
}
}seg;
int n,p,k;
ll arr[maxn];
int main()
{
int caset,t = 0;scanf("%d",&caset);
while(caset--) {
scanf("%d%d%d",&n,&p,&k);
seg.build(0,p-1,1);
arr[0] = 0;
for(int i=1;i<=n;i++) {
scanf("%lld",&arr[i]);
arr[i] += arr[i-1];
}
ll ans = -1;
for(int i=1;i<=n;i++) {
int l = (arr[i] % p),pos;
if(l > k) pos = seg.query(l-k,l,0,p-1,1);
else pos = 0;
if(pos != inf) ans = max(ans,(arr[i]-arr[pos])/p);
seg.update(l,i,0,p-1,1);
}
printf("Case %d: %lld\n",++t,ans);
}
return 0;
}
Solution2 Monotonicqueue S o l u t i o n 2 M o n o t o n i c q u e u e