bzoj2138: stone
Description
话说Nan在海边等人,预计还要等上M分钟。为了打发时间,他玩起了石子。Nan搬来了N堆石子,编号为1到N,每堆
包含Ai颗石子。每1分钟,Nan会在编号在[Li,Ri]之间的石堆中挑出任意Ki颗扔向大海(好疼的玩法),如果[Li,R
i]剩下石子不够Ki颗,则取尽量地多。为了保留扔石子的新鲜感,Nan保证任意两个区间[Li,Ri]和[Lj,Rj],不会
存在Li<=Lj&Rj<=Ri的情况,即任意两段区间不存在包含关系。可是,如果选择不当,可能无法扔出最多的石子,
这时NN就会不高兴了。所以他希望制定一个计划,他告诉你他m分钟打算扔的区间[Li,Ri]以及Ki。现在他想你告诉
他,在满足前i-1分钟都取到你回答的颗数的情况下,第i分钟最多能取多少个石子。
Input
第一行正整数N,表示石子的堆数;
第二行正整数x,y,z,P,(1<=x,y,z<=N;P<=500)
有等式A[i]=[(i-x)2+(i-y)2+(i-z)^2] mod P;
第三行正整数M,表示有M分钟;
第四行正整数K[1],K[2],x,y,z,P,(x,y,z<=1000;P<=10000)
有等式K[i]=(xK[i-1]+yK[i-2]+z)mod P。
接下来M行,每行两个正整数L[i],R[i]。
N<=40000 M<=N 1<=L[i]<=R[i]<=N A[i]<=500
Output
有M行,第i行表示第i分钟最多能取多少石子。
Sample Input
5
3 2 4 7
3
2 5 2 6 4 9
2 4
1 2
3 5
Sample Output
2
5
5
【样例说明】
石子每堆个数分别为0,5,2,5,0。
第1分钟,从第2到第4堆中选2个;
第2分钟,从第1到第2堆中选5个;
第3分钟,从第3到第5堆中选8个,但最多只能选5个。
分析
二分图的Hall定理:栋爷的博客
对于这道题,假设我们钦定每个区间要扔多少石子。那么剩下我们显然可以采用网络流是否满流验证这组解是否合法。
实际上这是一个二分图的多重匹配问题。
假设某个区间
[
l
i
,
r
i
]
[l_i,r_i]
[li,ri]要扔
k
i
k_i
ki 个石子,那么我们可以把这个区间拆成
k
i
k_i
ki个点分别连完全相同的边。同时把所有石子堆拆成
a
i
a_i
ai个点一样连完全相同的边。这样这就转化成了一个二分图的最大匹配问题。
首先将区间按左端点排序,编号,拆点之后假设是集合
∣
X
∣
|X|
∣X∣,石子是集合
∣
Y
∣
|Y|
∣Y∣,运用Hall定理现在我们要枚举集合
∣
X
∣
|X|
∣X∣的任意一个子集,判断每个点连出的边数是否大于等于集合大小。
Hall定理的几个套路
套路1
验证集合
∣
X
∣
|X|
∣X∣和
∣
Y
∣
|Y|
∣Y∣时,若能将
∣
X
∣
|X|
∣X∣和
∣
Y
∣
|Y|
∣Y∣分别分成两个子集
∣
X
S
∣
,
∣
X
T
∣
,
∣
Y
S
∣
,
∣
Y
T
∣
|X_S|,|X_T|,|Y_S|,|Y_T|
∣XS∣,∣XT∣,∣YS∣,∣YT∣,使得
∣
X
S
∣
|X_S|
∣XS∣不向
∣
Y
T
∣
|Y_T|
∣YT∣连边,
∣
X
T
∣
|X_T|
∣XT∣不向
∣
Y
S
∣
|Y_S|
∣YS∣连边,那么可以直接分别验证
∣
X
S
∣
,
∣
Y
S
∣
|X_S|,|Y_S|
∣XS∣,∣YS∣,
∣
X
T
∣
,
∣
Y
T
∣
|X_T|,|Y_T|
∣XT∣,∣YT∣是否合法。
这是显然的,因为两个不联通的块肯定是互不影响的。
应用到本题,两个不相交的区间可以分开验证。
套路2
在对应的
∣
X
∣
|X|
∣X∣集合不变的情况下,如果
Y
S
⊆
Y
T
Y_S\subseteq Y_T
YS⊆YT,那么若
∣
X
∣
,
∣
Y
T
∣
|X|,|Y_T|
∣X∣,∣YT∣合法,那么
∣
X
∣
,
∣
Y
S
∣
|X|,|Y_S|
∣X∣,∣YS∣一定合法。
显然
∣
X
∣
,
∣
Y
T
∣
|X|,|Y_T|
∣X∣,∣YT∣的约束条件更紧。
应用到本题。首先,一个区间拆出来的
k
i
k_i
ki个点可以一起验证,因为他们的连边情况相同。
其次,区间按左端点排序之后,假设验证两个编号不相邻的相交区间
x
,
y
x,y
x,y,假设
x
<
z
<
y
x<z<y
x<z<y那么考虑添加
z
z
z,由于区间互不包含的性质,所以显然
r
x
<
r
z
<
r
y
r_x<r_z<r_y
rx<rz<ry,因此,添加区间
z
z
z不会导致
∣
X
∣
|X|
∣X∣集合的变化,但是
∣
Y
∣
|Y|
∣Y∣集合增多,所以可以
x
,
z
,
y
x,z,y
x,z,y三个区间一起验证。
基于此,我们验证的一定是编号连续的若干个区间。
数据结构优化
写出式子。
假设钦定后每个区间取
b
i
b_i
bi个,我们要验证的即:
∀
l
,
r
∑
i
=
l
r
b
i
≤
∑
i
=
s
t
[
l
]
e
d
[
r
]
a
i
\forall l, r\sum_{i=l}^r b_i \le \sum_{i=st[l]}^{ed[r]} a_i
∀l,r∑i=lrbi≤∑i=st[l]ed[r]ai
假设
B
i
B_i
Bi是
b
i
b_i
bi的前缀和,
A
i
A_i
Ai是
a
i
a_i
ai的前缀和。
即
∀
l
,
r
B
r
−
B
l
−
1
≤
A
e
d
[
r
]
−
A
s
t
[
l
]
−
1
\forall l, r B_r-B_{l-1} \le A_{ed[r]}-A_{st[l]-1}
∀l,rBr−Bl−1≤Aed[r]−Ast[l]−1
∀
l
,
r
B
r
−
A
e
d
[
r
]
≤
B
l
−
1
−
A
s
t
[
l
]
−
1
\forall l, r B_r-A_{ed[r]} \le B_{l-1}-A_{st[l]-1}
∀l,rBr−Aed[r]≤Bl−1−Ast[l]−1
令
C
x
=
B
x
−
A
e
d
[
x
]
,
D
x
=
B
x
−
1
−
A
s
t
[
x
]
−
1
C_x=B_x-A_{ed[x]},D_x=B_{x-1}-A_{st[x]-1}
Cx=Bx−Aed[x],Dx=Bx−1−Ast[x]−1
即验证
∀
l
,
r
C
r
≤
D
l
(
l
<
r
)
\forall l, r C_r \le D_l(l<r)
∀l,rCr≤Dl(l<r)
考虑添加区间。对于还没有添加的区间,令
b
i
=
0
b_i=0
bi=0
对于区间
i
i
i,假设当前匹配了
x
x
x个,那么也就是说对于
C
C
C的
[
i
,
Q
]
[i,Q]
[i,Q]下标区间都会加上
x
x
x,
D
D
D的
[
i
+
1
,
Q
]
[i+1,Q]
[i+1,Q]下标区间会加上
x
x
x。
考虑影响。
- 当 r ∈ [ 1 , i − 1 ] r \in [1,i-1] r∈[1,i−1], l ∈ [ i + 1 , Q ] l \in [i+1,Q] l∈[i+1,Q]不满足 l < r l<r l<r
- 当 r ∈ [ 1 , i − 1 ] r \in [1,i-1] r∈[1,i−1], l ∈ [ 1 , i ] l \in [1,i] l∈[1,i]没有改变所以没有影响。
- 当 r ∈ [ i , Q ] r \in [i,Q] r∈[i,Q], l ∈ [ i + 1 , Q ] l \in [i+1,Q] l∈[i+1,Q]由于都加上了 x x x,原来满足现在仍然满足。
- r ∈ [ i , Q ] r \in [i,Q] r∈[i,Q], l ∈ [ 1 , i ] l \in [1,i] l∈[1,i],需要满足 ∀ l ∈ [ 1 , Q ] , r ∈ [ 1 , i ] , C r + x ≤ D l \forall l\in [1,Q],r\in [1,i], C_r+x \le D_l ∀l∈[1,Q],r∈[1,i],Cr+x≤Dl,也就是 x ≤ M i n l = 1 i D l − M a x r = i Q ( C r ) x \le Min_{l=1}^iD_l-Max_{r=i}^Q(C_r) x≤Minl=1iDl−Maxr=iQ(Cr)
用线段树维护一下即可。
复杂度
O
(
m
l
o
g
m
)
O(mlogm)
O(mlogm)
呼呼,搞完啦!
分析
#include<bits/stdc++.h>
const int N = 40050;
int ri() {
char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int C[N], D[N], A[N], tag[N], id[N], a[N], p[N], n, Q;
struct Que {int l, r, k, id;} q[N];
bool cmp(Que a, Que b) {return a.l < b.l;}
long long sqr(long long x) {return x * x;}
int Op(int a, int b, bool p) {return (p ? a < b : a > b) ? a : b;}
struct Node {
int v[2], tag[2]; Node *ls, *rs; //v[0]mx v[1]mn
void Tag(int x, bool p) {v[p] += x; tag[p] += x;}
void Push() {
if(tag[0])
ls->Tag(tag[0], 0), rs->Tag(tag[0], 0), tag[0] = 0;
if(tag[1])
ls->Tag(tag[1], 1), rs->Tag(tag[1], 1), tag[1] = 0;
}
void Up() {v[0] = Op(ls->v[0], rs->v[0], 0); v[1] = Op(ls->v[1], rs->v[1], 1);}
void Build(int L, int R) {
if(L == R) {v[0] = C[L]; v[1] = D[L]; return ;}
int m = L + R >> 1;
(ls = new Node)->Build(L, m);
(rs = new Node)->Build(m + 1, R);
Up();
}
void Modify(int L, int R, int st, int ed, int x, bool p) {
if(st == L && R == ed) return Tag(x, p);
Push(); int m = L + R >> 1;
if(st <= m) ls->Modify(L, m, st, std::min(m, ed), x, p);
if(ed > m) rs->Modify(m + 1, R, std::max(st, m + 1), ed, x, p);
Up();
}
int Query(int L, int R, int st, int ed, bool p) {
if(st == L && R == ed) return v[p];
Push(); int m = L + R >> 1;
if(ed <= m) return ls->Query(L, m, st, ed, p);
if(st > m) return rs->Query(m + 1, R, st, ed, p);
return Op(ls->Query(L, m, st, m, p), rs->Query(m + 1, R, m + 1, ed, p), p);
}
}rt;
void Init() {
n = ri();
int x = ri(), y = ri(), z = ri(), P = ri();
for(int i = 1;i <= n; ++i) a[i] = (sqr(i - x) + sqr(i - y) + sqr(i - z)) % P;
Q = ri(); if(!Q) exit(0);
q[1].k = ri(); q[2].k = ri();
x = ri(); y = ri(); z = ri(); P = ri();
for(int i = 3;i <= Q; ++i)
q[i].k = (1LL * q[i - 1].k * x + 1LL * q[i - 2].k * y + z) % P;
for(int i = 1;i <= Q; ++i)
q[i].l = ri(), q[i].r = ri(), q[i].id = i;
for(int i = 1;i <= Q; ++i) ++tag[q[i].l], --tag[q[i].r + 1];
int _n = 0;
for(int i = 1;i <= n; ++i) {
tag[i] += tag[i - 1];
if(tag[i]) id[i] = ++_n, a[_n] = a[i];
}
n = _n;
for(int i = 1;i <= n; ++i) A[i] = A[i - 1] + a[i];
for(int i = 1;i <= n; ++i) q[i].l = id[q[i].l], q[i].r = id[q[i].r];
std::sort(q + 1, q + Q + 1, cmp);
for(int i = 1;i <= Q; ++i) C[i] -= A[q[i].r], D[i] -= A[q[i].l - 1], p[q[i].id] = i;
}
int main() {
Init();
rt.Build(1, Q);
for(int i = 1;i <= Q; ++i) {
int j = p[i], d = rt.Query(1, Q, 1, j, 1), c = rt.Query(1, Q, j, Q, 0);
int x = std::min(d - c, q[j].k);
printf("%d\n", x);
rt.Modify(1, Q, j, Q, x, 0);
if(j != Q) rt.Modify(1, Q, j + 1, Q, x, 1);
}
return 0;
}