1 条题解
-
0
思路:二分答案,mid 是最后一条前面都不矛盾的条件。
check(mid)看 mid 及其前面的条件是否不矛盾,把这些条件复制一遍然后按最小值从大到小排序,然后按照最小值从大到小的顺序处理每个数。由于数字互不相同,最小值相同的那些条件的交集肯定是连续的一段(最小值就在这一段里);再算一下最小值相同(最小值设为 )的那些条件的并集,由于它们的交集应当是连续的一段,并集也肯定是连续的一段。处理到下一个最小值为 (更小,) 的条件时,前面最小值为 的条件们得出的交集和并集就拿去计算。
会维护一个并查集,并查集是对于 到 每一个数,维护这个数所在的连续区间的右端点的下一个位置。连续区间指的是一个由最小值更大()的那些条件算出来的一个大的并集的某连续一段。
如果刚刚处理的最小值为 的条件们算出的交集是这个大的并集的子集,即最小值更大的条件规定了这一段交集的区间的最小值是 的,然而这个交集规定这个区间的最小值是 ,这就矛盾了。
如果不矛盾,那么最小值为 的条件的并集就并入到大并集里,因为对于任意 , 的这些条件与 的这些条件,对 的条件们是等价的。
并入大并集时用并查集进行操作,从最小值为 的并集左端点开始,每个位置都连到下一个位置,这样经过路径压缩,每个位置最终都在这个位置所在的大并集的某个连续区间的右端点的下一个位置为根的集合内,这样每次就可以通过查交集的左端点的根节点是否大于交集的右端点判断交集是不是大并集的子集。
最后输出 。
(略潦草,长难句比较多;代码中注释只是我写的时候理思路用的,比较不容易看懂,仅供参考。)
#include<bits/stdc++.h> using namespace std; int n,q,f[1000005]; int fd(int u){ return u==f[u]?u:(f[u]=fd(f[u])); }//并查集维护的是大并集里的连续区间而非大并集本身,老大是并集内一段连续区间的尾部 struct nd{ int l,r,x; }a[25005],t[25005]; bool cmp(nd u,nd v){ if(u.x!=v.x)return u.x>v.x; return u.l<v.l; } bool check(int mid){ for(int i=1;i<=n+1;i++)f[i]=i; for(int i=1;i<=mid;i++)t[i]=a[i]; sort(t+1,t+1+mid,cmp); int lj,rj,lb,rb;//交和并是连续相等数算的,到了数不相等的时候参与完计算就重置了 lj=lb=t[1].l,rj=rb=t[1].r; for(int i=2;i<=mid;i++){ if(t[i].x==t[i-1].x){ lb=min(t[i].l,lb),lj=max(t[i].l,lj),rj=min(t[i].r,rj),rb=max(t[i].r,rb); if(lj>rj)return 0;//并集不在一起可以不用管,但是只要并集不在一起交集就不在一起,交集有问题就似了 } else{ if(fd(lj)>rj)return 0;//交集左端点如果在原来的大并集,它在的连续区间右端点大于交集右端点,就不行 int j=fd(lb); while(j<=rb)j=f[fd(j)]=fd(j+1);//左闭右开好处理 lj=lb=t[i].l,rj=rb=t[i].r; } } return fd(lj)<=rj;//26行 } int main(){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); cin>>n>>q; for(int i=1;i<=q;i++)cin>>a[i].l>>a[i].r>>a[i].x; int l=0,r=q+1; while(l<r-1){ int mid=l+r>>1; if(check(mid))l=mid; else r=mid; } if(r==q+1)cout<<0; else cout<<l+1;//or r? return 0; }
- 1
信息
- ID
- 1322
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 2
- 标签
- 递交数
- 66
- 已通过
- 40
- 上传者