洛谷 P2898 [USACO08JAN]haybale猜测Haybale Guessing 解题报告
[USACO08JAN]haybale猜测Haybale Guessing
题目描述
给一段长度为\(n\),每个位置上的数都不同的序列\(a[1\dots n]\)和\(q\)和问答,每个问答是\((x, y, r)\)代表\(\min_\limits{i=a}^ba_i= r\), 要你给出最早的有矛盾的那个问答的编号。
输入输出格式
输入格式:
Line 1: Two space-separated integers: \(N\) and \(Q\)
Lines 2..\(Q+1\): Each line contains three space-separated integers that represent a single query and its reply: \(Q_l\), \(Q_h\), and \(A\)
输出格式:
Line 1: Print the single integer \(0\) if there are no inconsistencies among the replies (i.e., if there exists a valid realization of the hay stacks that agrees with all Q queries). Otherwise, print the index from 1..Q of the earliest query whose answer is inconsistent with the answers to the queries before it.
牛客居然考原题...不过题目还是很棒的。
考虑什么情况会产生矛盾。
-
两段不相交的区间的最小值相同
-
一个最小值大的区间完全覆盖了一个最小值小的区间
-
一个区间被其他几个区间完全填充时的,最小值比它们都小。
直接处理很难,如果我们可以按顺序处理的话,比如按最小值从大到小排序,无疑会方便很多,但是回答本身就是带顺序的,我们不可以直接打乱它们。
怎么办呢,二分答案。这无疑是一个很棒的二分答案的思路。正确性很显然。
考虑判断前\(i\)个区间是否产生矛盾,我们按最小值从大到小排序,然后一个一个加入。
- 如果当前区间被其他区间完全覆盖,返回不合法。
- 如果当前区间与其他区间最小值相同
- 如果两个区间不交,返回不合法
- 如果两个区间有交,检查它们的交集是否被别人覆盖,拿它们的并集去覆盖别人。
检查区间覆盖可以通过线段树染色求得,只需要区间赋值\(1\)和区间求和就可以了。
Code:
#include <cstdio>
#include <algorithm>
#include <cstring>
#define rep(i,a,b) for(int i=a;i<=b;i++)
const int N=1e5+10;
int n,q,L[N],R[N],v[N];
struct node
{
int l,r,v,cl,cr;
bool friend operator <(node n1,node n2)
{
return n1.v==n2.v?n1.l<n2.l:n1.v>n2.v;
}
node(){}
node(int l,int r,int v)
{
this->l=l,this->r=r,this->v=v,this->cl=l,this->cr=r;
}
}Q[N],Q0[N];
const int M=4e6+10;
int tag[M],sum[M];
#define ls id<<1
#define rs id<<1|1
void pushdown(int id,int L,int R)
{
if(tag[id])
{
int Mid=L+R>>1;
sum[ls]=Mid+1-L,sum[rs]=R-Mid;
tag[id]=0;tag[ls]=tag[rs]=1;
}
}
int query(int id,int L,int R,int l,int r)
{
if(L==l&&R==r) return sum[id];
pushdown(id,L,R);
int Mid=L+R>>1;
if(r<=Mid) return query(ls,L,Mid,l,r);
else if(l>Mid) return query(rs,Mid+1,R,l,r);
else return query(ls,L,Mid,l,Mid)+query(rs,Mid+1,R,Mid+1,r);
}
void change(int id,int L,int R,int l,int r)
{
if(tag[id]) return;
if(L==l&&R==r)
{
sum[id]=R+1-L,tag[id]=1;
return;
}
int Mid=L+R>>1;
if(r<=Mid) change(ls,L,Mid,l,r);
else if(l>Mid) change(rs,Mid+1,R,l,r);
else change(ls,L,Mid,l,Mid),change(rs,Mid+1,R,Mid+1,r);
sum[id]=sum[ls]+sum[rs];
}
int max(int x,int y){return x>y?x:y;}
int min(int x,int y){return x<y?x:y;}
bool check(int m)
{
rep(i,1,m) Q[i]=node(L[i],R[i],v[i]);
std::sort(Q+1,Q+1+m);
int m_=0;
rep(i,1,m)
{
if(Q[i].v==Q0[m_].v)
{
if(Q0[m_].r<Q[i].l) return false;
else
{
Q0[m_].l=Q[i].l;
Q0[m_].cr=max(Q0[m_].cr,Q[i].r);
Q0[m_].r=min(Q0[m_].r,Q[i].r);
}
}
else Q0[++m_]=Q[i];
}
memset(tag,0,sizeof(tag));
memset(sum,0,sizeof(sum));
rep(i,1,m_)
{
int l=Q0[i].l,r=Q0[i].r,cl=Q0[i].l,cr=Q0[i].cr;
if(query(1,1,n,l,r)==r+1-l) return false;
change(1,1,n,cl,cr);
}
return true;
}
int main()
{
scanf("%d%d",&n,&q);
rep(i,1,q) scanf("%d%d%d",L+i,R+i,v+i);
int l=1,r=q;
if(check(q)) return puts("0"),0;
while(l<r)
{
int mid=l+r>>1;
if(check(mid)) l=mid+1;
else r=mid;
}
printf("%d\n",l);
return 0;
}
2018.10.29