bzoj1135 [POI2009]Lyz

来源:互联网 发布:excite翻译软件 编辑:程序博客网 时间:2024/06/05 18:37

【题意】

略。

【数据范围】

n<=200000,m<=500000,k<=10^9

【思路】

看成二分图匹配,左边人右边鞋,查询是否存在能将所有人全部匹配

Hall定理:

此定理使用于组合问题中,二部图G中的两部分顶点组成的集合分别为X, Y, X={X1, X2, X3,X4,.........,Xm}, Y={y1, y2, y3, y4 ,.........,yn},G中有一组无公共点的边,一端恰好为组成X的点的充分必要条件是:X中的任意k个点至少与Y中的k个点相邻。(1≤k≤m)

对于这个问题来说,有解当且仅当任取区间[L,R](1<=L<=R<=n-d),左边[L,R]中的人数<=其对应的鞋数,即:

a[L]+......+a[R]<=k*(R+d-L+1)

(a[L]-k)+......+(a[R]-k)<=k*d

对(a[i]-k)支持单点修改,查询最大子段和,使用线段树

【时间复杂度】

O(m log n)

#include<cstdio>#include<cstring>#include<algorithm>#define N 200010#define ll long longusing namespace std; struct tt{int a, b, l, r; ll mn, lmn, rmn, s;}t[N*2];int n, m, k, d, x, y, l;ll sum; inline int read(){    int x=0, f=1; char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}    return x*f;} void maketree(int L, int R){    t[l].a=L; t[l].b=R; t[l].l=t[l].r=0;    t[l].mn=t[l].lmn=t[l].rmn=0; t[l].s=(ll)k*(R-L+1);    if(L<R){        int mid=(L+R)>>1, l1=l;        l++; t[l1].l=l; maketree(L, mid);        l++; t[l1].r=l; maketree(mid+1, R);    }} void ins(int i, int x, int y){    if(t[i].a==t[i].b){        t[i].s+=y; t[i].mn=t[i].lmn=t[i].rmn=min(t[i].s, 0LL);        return;    }    int mid=(t[i].a+t[i].b)>>1;    if(x<=mid)ins(t[i].l, x, y); else ins(t[i].r, x, y);    t[i].s=t[t[i].l].s+t[t[i].r].s;    t[i].lmn=min(t[t[i].l].lmn, t[t[i].l].s+t[t[i].r].lmn);    t[i].rmn=min(t[t[i].r].rmn, t[t[i].r].s+t[t[i].l].rmn);    t[i].mn=min(t[t[i].l].mn, t[t[i].r].mn);    t[i].mn=min(t[i].mn, t[t[i].l].rmn+t[t[i].r].lmn);} int main(){    n=read(); m=read(); k=read(); d=read(); sum=(ll)k*d;    l=1; maketree(1, n-d);    for(int i=1; i<=m; i++){        x=read(); y=read();        ins(1, x, -y);        if(t[1].mn<-sum)printf("NIE\n"); else printf("TAK\n");    }    return 0;}


0 0