题目大意:N个数排成一列,有q个询问,每个询问告诉你区间[l,r]的最小值是多少(这N个数各不相同)。问你这q个询问有没有矛盾,有的话从哪里开始有矛盾
题解:二分答案,现在检验前mid个询问是否有矛盾
Orz并查集神奇做法
我的收获:并查集强啊
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXN 1100000
using namespace std;
struct Query
{
int L,R,ans;
}querys[MAXN],tmp[MAXN];
bool cmp(Query a,Query b)
{
return a.ans>b.ans;
}
int n,q;
int f[MAXN],stack[MAXN];
int findSet(int x)
{
int top=0;
while(f[x]!=x)
{
stack[++top]=x;
x=f[x];
}
for(int i=1;i<=top;i++) f[stack[i]]=x;
return x;
}
bool check(int pos) //判断第pos个询问是否和之前的有矛盾
{
for(int i=1;i<=pos;i++) tmp[i]=querys[i];
sort(tmp+1,tmp+pos+1,cmp);
//printf("-----------\n");
//for(int i=1;i<=pos;i++) printf("%d %d %d\n",tmp[i].L,tmp[i].R,tmp[i].ans);
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1,j=1;i<=pos;i=j+1,j++) //!!!!!!
{
int La,Ra,Lb,Rb; //这段区间的并集区间是[La,Ra],交集区间是[Lb,Rb]
La=Lb=tmp[i].L,Ra=Rb=tmp[i].R; //!!!!!
while(j<pos&&tmp[j+1].ans==tmp[i].ans) //!!!!!!
{
j++;
La=min(La,tmp[j].L); //!!!!我去。。。全都把tmp打成query了
Ra=max(Ra,tmp[j].R); //!!!!
Lb=max(Lb,tmp[j].L); //!!!!
Rb=min(Rb,tmp[j].R); //!!!!
}
if(findSet(Rb)+1<=Lb) return true;
for(int t=Ra,p=findSet(t);p>=La;t=p-1,p=findSet(t)) //!!!!!注意是t=p-1不是t=p,原因可以自己模拟下合并过程,就会发现
f[p]=La-1;
}
return false;
}
int main()
{
scanf("%d%d",&n,&q);
for(int i=1;i<=q;i++) scanf("%d%d%d",&querys[i].L,&querys[i].R,&querys[i].ans);
//check(6);
int lowerBound=1,upperBound=q,ans=-1;
while(lowerBound<=upperBound)
{
int mid=(lowerBound+upperBound)>>1;
if(check(mid))
{
ans=mid;
upperBound=mid-1;
}
else lowerBound=mid+1;
}
if(ans==-1) printf("0\n");
else printf("%d\n",ans);
return 0;
}