bzoj3693: 圆桌会议【线段树+Hall定理】

Description

有n组人要一起开一个圆桌会议(编号为0~n-1),会议的圆桌上有m个位置(编号为0~m-1)。每个组有ai个人,他
们需要被安排在(li,(li+1)%m,(li+2)%m,…,ri)的座位范围内。每个座位只能安排一个人就坐,并且每个人
都需要被安排一个座位。现在你需要判断是否存在满足条件的座位安排。

Input

输入包含不超过10组数据。
第一行有一个数字T,表示数据组数。
接下来有T组数据,每组数据第一行包含两个数n,m,表示有多少组的人与圆桌的位置数。
每组数据接下来包含n行,每行包含3个数li,ri,ai。

Output

对于每组数据,输出”Yes”或”No”,表示是否存在符合条件的安排。

Sample Input

2

2 4

0 1 2

1 2 2

2 3

2 0 2

1 1 1

Sample Output

No

Yes

HINT

T≤10,其中有不超过3组的数据范围为n≤10^5,m≤10^9。

解题思路:

相当于求一个二分图匹配,使得一组点全部被包含。

Hall定理——对于任意的二分图G,G的两个部分为X={x1,x2,…,xn}和Y={y1,y2,…,ym},存在一个匹配M使得|M|=|X|的充要条件为对于X的任意一个子集A,与A相邻的点集记为T(A),一定有|T(A)|≥|A|

那么根据Hall定理,这道题的匹配有解就是相当于对于任意位置区间[L,R],找出所有包含于它的条件,则ai之和<=R-L+1,即ai之和+L<=R+1。

而显然这道题需要考虑的区间只有题目给定区间的任意并集(网上很多题解包含的交集,但交集没有讨论的必要)。

那么将所有区间按照右端点升序排序并顺次扫描,维护每个左端点对应L+ai之和,问题转化为区间+ai和区间求max,(离散化)线段树维护即可。

环上的话就把环倍长成链,Li<=Ri的区间视为[Li,Ri]、[Li+m,Ri+m]两个区间,Li>Ri的区间视为[Li,Ri+m],后面同链上一样,排序+线段树处理。

但这样有可能在询问[Li,Ri+m] (且Ri=Li-1)一类的区间时,[Li,Ri]、[Li+m,Ri+m]的这种区间被断在了首尾两端,不过这种询问区间长度都是m,开始时直接特判ai总和是否小于m即可。

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')f=-1,c=getchar();
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}

const int N=200005;
int T,n,m,cnt,Mx,sum,b[N],mx[N<<2],add[N<<2];
struct node
{
    int l,r,x;
    inline friend bool operator < (const node &a,const node &b)
    {return a.r<b.r;}
}q[N];

void build(int k,int l,int r)
{
    add[k]=0;
    if(l==r){mx[k]=b[l];return;}
    int mid=l+r>>1;
    build(k<<1,l,mid),build(k<<1|1,mid+1,r);
    mx[k]=max(mx[k<<1],mx[k<<1|1]); 
}

void pushdown(int k)
{
    mx[k<<1]+=add[k],mx[k<<1|1]+=add[k];
    add[k<<1]+=add[k],add[k<<1|1]+=add[k];
    add[k]=0;
}

void modify(int k,int l,int r,int x,int y,int v)
{
    if(l==x&&r==y){mx[k]+=v,add[k]+=v;return;}
    if(add[k])pushdown(k);
    int mid=l+r>>1;
    if(y<=mid)modify(k<<1,l,mid,x,y,v);
    else if(x>mid)modify(k<<1,mid+1,r,x,y,v);
    else modify(k<<1,l,mid,x,mid,v),modify(k<<1|1,mid+1,r,mid+1,y,v);
    mx[k]=max(mx[k<<1],mx[k<<1|1]);
}

int query(int k,int l,int r,int x,int y)
{
    if(l==x&&r==y)return mx[k];
    if(add[k])pushdown(k);
    int mid=l+r>>1;
    if(y<=mid)return query(k<<1,l,mid,x,y);
    else if(x>mid)return query(k<<1|1,mid+1,r,x,y);
    else return max(query(k<<1,l,mid,x,mid),query(k<<1|1,mid+1,r,mid+1,y));
}

int main()
{
    //freopen("lx.in","r",stdin);
    T=getint();
    while(T--)
    {
        n=getint(),m=getint(),sum=Mx=cnt=0;
        for(int i=1;i<=n;i++)
        {
            int l=getint(),r=getint(),x=getint();b[++Mx]=l,sum+=x;
            if(l<=r)q[++cnt]=(node){l,r,x},q[++cnt]=(node){l+m,r+m,x},b[++Mx]=l+m;
            else q[++cnt]=(node){l,r+m,x};
        }
        if(sum>m){puts("No");continue;}
        sort(q+1,q+cnt+1);
        sort(b+1,b+Mx+1);
        Mx=unique(b+1,b+Mx+1)-b-1;
        for(int i=1;i<=cnt;i++)q[i].l=lower_bound(b+1,b+Mx+1,q[i].l)-b;
        build(1,1,Mx);bool flag=1;
        for(int i=1;i<=cnt;i++)
        {
            modify(1,1,Mx,1,q[i].l,q[i].x);
            int tmp=query(1,1,Mx,1,q[i].l);
            if(tmp>q[i].r+1){flag=0;break;}
        }
        flag?puts("Yes"):puts("No");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值