考试的时候打了八十分暴力QAQ虽然八十分过了,但是算法有bug。。数据没有卡我算法,然后就水过了8个点。。害怕。
正解:
二分+并查集
二分前mid个是否有矛盾的,特判一下全部符合的,答案为n+1
验证答案时,用并查集维护。
首先,如果已知一段区间[a,b]的最小值为x1,再出现一组猜测:[c,d]的最小值为x2。
① 若x1 > x2且区间[c,d] 包含在[a,b]里,则自相矛盾。
② 若x1==x2且区间[c,d]和[a,b]没有交集,则自相矛盾。
③ 若x1 < x2且区间[a,b] 包含在[c,d]里,则自相矛盾。
为了简化问题,可以把每一组猜测按照数字大小排序,这样就可以只处理其中的两种情况。
假设现在按照从大到小排序,从头到尾枚举:
如果遇见①②两种情况,则自相矛盾
记录目前可以确定的最小值为某个数的最大区间和最小区间。维护四个变量:lmin,lmax,rmin,rmax
对于最大的区间最小值为某一个数的区间[l,r],将它包含的所有的长度为1的区间的fa数组指向r+1(至于为什么不是指向r,等会再说)。
处理到与上一个数值相同的,就更新四个变量,每次更新如果发现不符合②,直接return false
若与上一个的数值不同,查看是否是①中的情况(当前集合的父亲是否大于rmin),如果不是,就把目前已知的区间最小值为上一个数字的最大的区间合并起来。
注:
指向r会有bug。如果指向r,则应该判断当前集合的父亲是否大于等于rmin,但是如果像这样的情况:最后一组是7 7 1,也就是左右端点相等,判断等于,就会返回false
具体看代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1000000+10;
int n,t,ans;
int fa[maxn];
struct hh
{
int l,r,x;
}e[maxn],a[maxn];
bool cmp(hh x,hh y)
{
return x.x>y.x;
}
int find(int x)
{
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
bool check(int mid)
{
int lmin,lmax,rmin,rmax;
for(int i=1;i<=mid;++i) a[i]=e[i];
for(int i=1;i<=n+5;++i) fa[i]=i;
sort(a+1,a+mid+1,cmp);
for(int i=1;i<=mid+1;++i)
{
if(i==1)
{
lmin=lmax=a[i].l;
rmin=rmax=a[i].r;
}
else if(i==mid+1)
{
if(find(lmax)>rmin) return false;
return true;
}
else if(a[i].x==a[i-1].x)
{
lmin=min(lmin,a[i].l);
lmax=max(lmax,a[i].l);
rmin=min(rmin,a[i].r);
rmax=max(rmax,a[i].r);
if(lmax>rmin) return false;
}
else
{
if(find(lmax)>rmin) return false;
for(int j=lmin;j<=rmax;++j)
fa[find(j)]=find(rmax+1);
lmin=lmax=a[i].l;
rmin=rmax=a[i].r;
}
}
}
int main()
{
scanf("%d%d",&n,&t);
for(int i=1;i<=t;++i) scanf("%d%d%d",&e[i].l,&e[i].r,&e[i].x);
int l=0,r=t+1;
while(r-l>1)
{
int mid=(l+r)>>1;
if(check(mid)) l=mid;
else ans=r=mid;
}
if(l==t&&check(t)) ans=t+1;
printf("%d",ans);
return 0;
}