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");
}
}