bzoj2138: stone Hall定理 线段树

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lvzelong2014/article/details/83717476

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] 要扔 k i k_i 个石子,那么我们可以把这个区间拆成 k i k_i 个点分别连完全相同的边。同时把所有石子堆拆成 a i a_i 个点一样连完全相同的边。这样这就转化成了一个二分图的最大匹配问题。
首先将区间按左端点排序,编号,拆点之后假设是集合 X |X| ,石子是集合 Y |Y| ,运用Hall定理现在我们要枚举集合 X |X| 的任意一个子集,判断每个点连出的边数是否大于等于集合大小。

Hall定理的几个套路

套路1

验证集合 X |X| Y |Y| 时,若能将 X |X| Y |Y| 分别分成两个子集 X S , X T , Y S , Y T |X_S|,|X_T|,|Y_S|,|Y_T| ,使得 X S |X_S| 不向 Y T |Y_T| 连边, X T |X_T| 不向 Y S |Y_S| 连边,那么可以直接分别验证 X S , Y S |X_S|,|Y_S| X T , Y T |X_T|,|Y_T| 是否合法。
这是显然的,因为两个不联通的块肯定是互不影响的。
应用到本题,两个不相交的区间可以分开验证。

套路2

在对应的 X |X| 集合不变的情况下,如果 Y S Y T Y_S\subseteq Y_T ,那么若 X , Y T |X|,|Y_T| 合法,那么 X , Y S |X|,|Y_S| 一定合法。
显然 X , Y T |X|,|Y_T| 的约束条件更紧。
应用到本题。首先,一个区间拆出来的 k i k_i 个点可以一起验证,因为他们的连边情况相同。
其次,区间按左端点排序之后,假设验证两个编号不相邻的相交区间 x , y x,y ,假设 x &lt; z &lt; y x&lt;z&lt;y 那么考虑添加 z z ,由于区间互不包含的性质,所以显然 r x &lt; r z &lt; r y r_x&lt;r_z&lt;r_y ,因此,添加区间 z z 不会导致 X |X| 集合的变化,但是 Y |Y| 集合增多,所以可以 x , z , y x,z,y 三个区间一起验证。
基于此,我们验证的一定是编号连续的若干个区间。

数据结构优化

写出式子。
假设钦定后每个区间取 b i b_i 个,我们要验证的即:
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
假设 B i B_i b i b_i 的前缀和, A i A_i a i a_i 的前缀和。
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 , 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}
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}
即验证 l , r C r D l ( l &lt; r ) \forall l, r C_r \le D_l(l&lt;r)
考虑添加区间。对于还没有添加的区间,令 b i = 0 b_i=0
对于区间 i i ,假设当前匹配了 x x 个,那么也就是说对于 C C [ i , Q ] [i,Q] 下标区间都会加上 x x D D [ i + 1 , Q ] [i+1,Q] 下标区间会加上 x x
考虑影响。

  • r [ 1 , i 1 ] r \in [1,i-1] l [ i + 1 , Q ] l \in [i+1,Q] 不满足 l &lt; r l&lt;r
  • r [ 1 , i 1 ] r \in [1,i-1] l [ 1 , i ] l \in [1,i] 没有改变所以没有影响。
  • r [ i , Q ] r \in [i,Q] l [ i + 1 , Q ] l \in [i+1,Q] 由于都加上了 x x ,原来满足现在仍然满足。
  • r [ i , Q ] r \in [i,Q] l [ 1 , i ] l \in [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 ,也就是 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)

用线段树维护一下即可。
复杂度 O ( m l o g m ) 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;
}


猜你喜欢

转载自blog.csdn.net/lvzelong2014/article/details/83717476
今日推荐