1. 程式人生 > >洛谷 P2898 [USACO08JAN]haybale猜測Haybale Guessing 解題報告

洛谷 P2898 [USACO08JAN]haybale猜測Haybale Guessing 解題報告

gree 區間 ots 長度 兩個 min -m spa 別人

[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.


牛客居然考原題...不過題目還是很棒的。

考慮什麽情況會產生矛盾。

  1. 兩段不相交的區間的最小值相同
  2. 一個最小值大的區間完全覆蓋了一個最小值小的區間

  3. 一個區間被其他幾個區間完全填充時的,最小值比它們都小。

直接處理很難,如果我們可以按順序處理的話,比如按最小值從大到小排序,無疑會方便很多,但是回答本身就是帶順序的,我們不可以直接打亂它們。

怎麽辦呢,二分答案。這無疑是一個很棒的二分答案的思路。正確性很顯然。

考慮判斷前\(i\)個區間是否產生矛盾,我們按最小值從大到小排序,然後一個一個加入。

  1. 如果當前區間被其他區間完全覆蓋,返回不合法。
  2. 如果當前區間與其他區間最小值相同
    • 如果兩個區間不交,返回不合法
    • 如果兩個區間有交,檢查它們的交集是否被別人覆蓋,拿它們的並集去覆蓋別人。

檢查區間覆蓋可以通過線段樹染色求得,只需要區間賦值\(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

洛谷 P2898 [USACO08JAN]haybale猜測Haybale Guessing 解題報告