$bzoj1135$

$Hall定理+线段树$

$Hall定理:对于有完美匹配的二分图,从全部匹配的那一侧选择k个元素,另一侧至少有k个元素与其相连,这里指的是元素的并集,这个是充要条件,也就是我们可以用这个条件判断二分图是否有完美匹配,这里就用到了。完美匹配就是指全部匹配了$

$题目里人和鞋构成二分图,要求人有完美匹配,那么我们运用Hall定理,只要任意选出k个人相邻的鞋至少有k双就行了,但是如果直接枚举的话复杂度是指数级的$

$观察一下发现,事实上如果任意连续的一段人满足了,那么不连续的肯定满足,因为每个人对应的鞋是[x,x+k],那么对于一段连续的人我们挖掉中间的一个人不会让对应的鞋的集合变小,所以我们只用看连续的人就可以了,用线段树检查,但是复杂度是O(n^{2}logn)的$

$继续观察,事实上可以化简成这样的一个式子\sum_{i=l}^{r}{a[i]}\leq{(r-l+k+1)*d}$

$然后就很简单了,相当于\sum_{i=l}^{r}{a[i]-k}\leq{k*d}$

$由于k*d是定值,那么我们用线段树维护左边的式子,查询最大子段和就行了$

 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
int n, m, k, d;
ll l[N << 2], r[N << 2], sum[N << 2], mx[N << 2];
void update(int L, int R, int x, int p, int d) {
    if(L == R) {
        mx[x] += d;
        sum[x] += d;
        l[x] += d;
        r[x] += d;
        return;
    }
    int mid = (L + R) >> 1;
    if(p <= mid) {
        update(L, mid, x << 1, p, d);
    } else {
        update(mid + 1, R, x << 1 | 1, p, d);
    }
    sum[x] = sum[x << 1] + sum[x << 1 | 1];
    l[x] = max(l[x << 1], sum[x << 1] + l[x << 1 | 1]);
    r[x] = max(r[x << 1 | 1], sum[x << 1 | 1] + r[x << 1]);
    mx[x] = max(mx[x << 1], max(mx[x << 1 | 1], r[x << 1] + l[x << 1 | 1]));
}
void build(int L, int R, int x) {
    if(L == R) {
        mx[x] = l[x] = r[x] = sum[x] = -k;
        return;
    }
    int mid = (L + R) >> 1;
    build(L, mid, x << 1);
    build(mid + 1, R, x << 1 | 1);
    sum[x] = sum[x << 1] + sum[x << 1 | 1];
    l[x] = max(l[x << 1], sum[x << 1] + l[x << 1 | 1]);
    r[x] = max(r[x << 1 | 1], sum[x << 1 | 1] + r[x << 1]);
    mx[x] = max(mx[x << 1], max(mx[x << 1 | 1], r[x << 1] + l[x << 1 | 1]));
}
int main() {
    scanf("%d%d%d%d", &n, &m, &k, &d);
    build(1, n, 1);
    while(m--) {
        int r, x;
        scanf("%d%d", &r, &x);
        update(1, n, 1, r, x);
        puts(mx[1] <= (ll)d * k ? "TAK" : "NIE");
    }
    return 0;
} 
View Code

 

posted @ 2018-01-27 20:50  19992147  阅读(111)  评论(0编辑  收藏  举报