3693: 圆桌会议
3693: 圆桌会议
https://www.lydsy.com/JudgeOnline/problem.php?id=3693
分析:
Hall定理+线段树。
如果将桌子放到左边,每组的人拆开放到右边,就成了二分图匹配问题,问是否存在完美匹配。
Hall定理:设二分图中G=<V1,V2,E>中 |V1|=m<=|V2|=n,G中存在从V1到V2的完全匹配当且仅当V1中任意k(k=1,2,...,m)个顶点至少与V2中k个顶点是相邻的。
但是此题中,判断左边所有的Li,Ri就行了。
首先如果左边的桌子没有被一些人选到(即没有边),显然不用考虑。然后只去考虑剩下的点的子集复杂度也是2^n级别的。剩下的许多桌子一定是被一些组的人选到了。考虑一组人选到的桌子Li,Ri,那么这些桌子向右边对部分人连的边是相同的(每个桌子都连向这组的每个人),那么只要判一下这个区间是否满足Ri-Li+1>=Σai(连向右边的点的点数)就行了。这个区间的子集就可以不用考虑了,因为每个点连出的边都一样。然后在查一下多个区间和来的情况,就是最小的l,和最大的r之间。
所以查询的时候只需要查任意的L与任意的R就行了。然后查询就是R-L+1>=s(s为L~R这些点连向右边的点数),R>=s+L-1,然后对于每个Ri查询所有小于Ri的L,是否满足这个式子。线段树维护这个值,每次查询所有小于Li的L的最大值。加入一个Ri,加入一个区间Li~Ri,然后所有的小于Li的L都应更新加上ai,表示L~Ri的向右边连的点数增加了ai。
代码:
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<cmath> 5 #include<iostream> 6 #include<cctype> 7 #include<set> 8 #include<vector> 9 #include<queue> 10 #include<map> 11 #define fi(s) freopen(s,"r",stdin); 12 #define fo(s) freopen(s,"w",stdout); 13 using namespace std; 14 typedef long long LL; 15 16 inline int read() { 17 int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; 18 for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; 19 } 20 21 const int N = 200005; 22 const int M = 400005; 23 struct Que{ 24 int l,r,v; 25 Que() {} 26 Que(int a,int b,int c) { l = a, r = b, v = c; } 27 bool operator < (const Que &A) const { 28 return r < A.r; 29 } 30 }q[N]; 31 int Num[N << 1]; 32 int mx[M << 2], tag[M << 2]; 33 34 #define Root 1, n, 1 35 #define lson l, mid, rt << 1 36 #define rson mid + 1, r, rt << 1 | 1 37 38 void pushup(int rt) { 39 mx[rt] = max(mx[rt << 1], mx[rt << 1 | 1]); 40 } 41 void pushdown(int rt) { 42 if (tag[rt]) { 43 mx[rt << 1] += tag[rt]; tag[rt << 1] += tag[rt]; 44 mx[rt << 1 | 1] += tag[rt]; tag[rt << 1 | 1] += tag[rt]; 45 tag[rt] = 0; 46 } 47 } 48 void build(int l,int r,int rt) { 49 tag[rt] = mx[rt] = 0; 50 if (l == r) { 51 mx[rt] = Num[l] - 1; 52 return; 53 } 54 int mid = (l + r) >> 1; 55 build(lson); build(rson); 56 pushup(rt); 57 } 58 void update(int l,int r,int rt,int L,int R,int v) { 59 if (L <= l && r <= R) { 60 mx[rt] += v; tag[rt] += v; 61 return ; 62 } 63 pushdown(rt); 64 int mid = (l + r) >> 1; 65 if (L <= mid) update(lson, L, R, v); 66 if (R > mid) update(rson, L, R, v); 67 pushup(rt); 68 } 69 int query(int l,int r,int rt,int L,int R) { 70 if (L <= l && r <= R) { 71 return mx[rt]; 72 } 73 pushdown(rt); 74 int mid = (l + r) >> 1, res = 0; 75 if (L <= mid) res = query(lson, L, R); 76 if (R > mid) res = max(res, query(rson, L, R)); 77 return res; 78 } 79 80 void solve() { 81 int n = read(), m = read(); 82 LL sum = 0; 83 int Q = 0; 84 for (int i=1; i<=n; ++i) { 85 int l = read() + 1, r = read() + 1, v = read(); 86 q[++Q] = Que(l, r, v); 87 if (l > r) q[Q].r += m; 88 else q[++Q] = Que(l + m, r + m, v); 89 sum += q[Q].v; 90 } 91 if (sum > m) { 92 puts("No"); return ; 93 } 94 95 int cnt = 0; 96 for (int i=1; i<=Q; ++i) Num[++cnt] = q[i].l, Num[++cnt] = q[i].r; 97 sort(Num + 1, Num + cnt + 1); 98 int lim = cnt; cnt = 1; 99 for (int i=2; i<=lim; ++i) if (Num[cnt] != Num[i]) Num[++cnt] = Num[i]; 100 for (int i=1; i<=Q; ++i) { 101 q[i].l = lower_bound(Num + 1, Num + cnt + 1, q[i].l) - Num; 102 q[i].r = lower_bound(Num + 1, Num + cnt + 1, q[i].r) - Num; 103 } 104 105 n = cnt; 106 sort(q + 1, q + Q + 1); 107 build(Root); 108 for (int i=1; i<=Q; ++i) { 109 update(Root, 1, q[i].l, q[i].v); 110 if (query(Root, 1, q[i].l) > Num[q[i].r]) { 111 puts("No"); return; 112 } 113 } 114 puts("Yes"); 115 } 116 int main() { 117 int T = read(); 118 while (T--) solve(); 119 return 0; 120 }