[USACO08JAN]Haybale Guessing G
并查集做法确实很巧妙,但实际上最优解是线段树
目前本人luogu最优解第三,此处讲一下线段树的做法
首先二分不再赘述
矛盾的条件
- 同一数值,多次出现,几个区间间交集为空集(因为每个数值存在一个)
- 一个数值出现时,它的区间已经被较大值的区间覆盖(此时本数值放在区间内任意位置,都会使之前给出的信息不成立)
具体的操作:
- 预处理 对数值离散化
- 先把重复数值处理,求交集和并集(它的交集是它可以存在的区间,它的并集是它可以覆盖的区间),若无交集则矛盾
- 对二分取定的前 K 条信息进行排序,先处理较大数值(因为每个数值会不会被覆盖只跟大于它的数有关)
- 访问到某条信息时,先在线段树上查询本区间是否有位置未被覆盖, 若有则该数值还有生存空间,若无返回 0 ,然后修改它的区间
存储
众所周知这是个区间覆盖的问题,但是一个较小值的区间被哪个值覆盖并不重要, 所以线段树里只需要存 bool , 被覆盖则为 真 ,否则为 假
然后,因为要查询的是是否有未被覆盖的位置, 所以存的是区间所有数值的与, 也就是
\[t[k] = t[lt]\,\&\,t[rt]
\]
修改&查询
- 用交集查询,用并集修改
- 修改时的操作全部都是 \(t[k]\,\|\,=1\)
因为递归时当前树上节点对应区间越来越小,所以若当前节点值为 真 ,再往下也都是 真 ,所以就不用再往下递归 - 查询时同上条,若当前节点是 真 ,那么不再递归
- 当前节点值为 真 ,说明它子树节点都会是 真 ,仔细想想,这与 延迟标记 不是一样吗?所以节点值可以兼职 延迟标记
记得建树初始化(不用 pushup )
丑陋的代码:
#include<cstdio>
#include<cstring>
#include<cctype>
#include<vector>
#include<algorithm>
#define reg register int
#define ttT template <typename T>
#define EL puts("Elaina")
using namespace std;
inline char gc(){
static char buf[1<<20],*p1,*p2;
if(p1==p2){p1=buf,p2=buf+fread(buf,1,1<<20,stdin);if(p1==p2)return EOF;}
return *p1++;
}
ttT inline void read(T &x){
x=0;int f=1;char ch=gc();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
x*=f;
}
ttT inline void write(T x){
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar((x%10)^48);
}
const int maxn=1e6+3,maxm=25000+3,INF=0x7fffffff;
int n,m,b[maxm];
struct qestion{
int l,r,v;
}a[maxm];
struct nump{
int l,r;//交
int l2,r2;//并
}c[maxm];//下标为数值(草堆数)
#define lt (k<<1)
#define rt (k<<1|1)
struct tree{
int l,r;
bool v;
}t[maxn<<2];
inline void pushup(int k){
t[k].v=t[lt].v&t[rt].v;
}
inline void pushdown(int k){
if(t[k].v)t[lt].v=t[rt].v=true;
}
inline void build(int k,int l,int r){
t[k].l=l,t[k].r=r,t[k].v=false;
if(l==r)return;
else{
int mid=(l+r)>>1;
build(lt,l,mid);
build(rt,mid+1,r);
}
}
inline bool query(int L,int R,int k){
if(t[k].v)return true;
if(L<=t[k].l&&t[k].r<=R)return t[k].v;
else{
pushdown(k);
bool ans=true;
if(L<=t[lt].r)ans&=query(L,R,lt);
if(R>=t[rt].l)ans&=query(L,R,rt);
return ans;
}
}
inline void updata(int L,int R,int k){
if(t[k].v)return;
if(L<=t[k].l&&t[k].r<=R)t[k].v=true;
else{
pushdown(k);
if(L<=t[lt].r)updata(L,R,lt);
if(R>=t[rt].l)updata(L,R,rt);
pushup(k);
}
}
/*
1、相同数字,多次出现,某两个区间不重叠
2、修改后失去某个数值
对数值 离散化
修改:较大值覆盖较小值
*/
inline bool check(int k){
if(!k)return true;
memset(c,0,sizeof(c));
vector <int> apr;//出现过的数值
int minv=INF,maxv=-INF;
for(reg i=1;i<=k;++i){
int v=a[i].v;
minv=min(minv,a[i].l),maxv=max(maxv,a[i].r);//需要建树的区间
if(!c[v].l){
c[v].l=c[v].l2=a[i].l,c[v].r=c[v].r2=a[i].r;
apr.push_back(v);
}
else{
c[v].l=max(c[v].l,a[i].l),c[v].r=min(c[v].r,a[i].r);
if(c[v].l>c[v].r)return false;
c[v].l2=min(c[v].l2,a[i].l),c[v].r2=max(c[v].r2,a[i].r);
}
}
sort(apr.begin(),apr.end());
build(1,minv,maxv);
for(reg i=apr.size()-1;i>=0;--i){
int v=apr[i];
if(query(c[v].l,c[v].r,1))return false;
updata(c[v].l2,c[v].r2,1);
}
return true;
}
void solve(){
read(n),read(m);
for(reg i=1;i<=m;++i){
read(a[i].l),read(a[i].r),read(a[i].v);
b[i]=a[i].v;
}
sort(b+1,b+m+1);
int lb=unique(b+1,b+m+1)-b-1;
for(reg i=1;i<=m;++i)
a[i].v=lower_bound(b+1,b+lb+1,a[i].v)-b;
int l=0,r=m,mid,ans;
while(l<=r){
mid=(l+r)>>1;
if(check(mid))ans=mid,l=mid+1;
else r=mid-1;
}
if(ans==m)ans=-1;
write(ans+1),puts("");
}
int main(){
solve();
return 0;
}