bzoj1595&luogu2898[Usaco2008 Jan]猜数游戏

78 篇文章 0 订阅
41 篇文章 0 订阅

http://www.elijahqi.win/archives/1480
题目描述

The cows, who always have an inferiority complex about their intelligence, have a new guessing game to sharpen their brains.

A designated ‘Hay Cow’ hides behind the barn and creates N (1 ≤ N ≤ 1,000,000) uniquely-sized stacks (conveniently numbered 1..N) of hay bales, each with 1..1,000,000,000 bales of hay.

The other cows then ask the Hay Cow a series of Q (1 ≤ Q ≤ 25,000) questions about the the stacks, all having the same form:

What is the smallest number of bales of any stack in the range of stack numbers Ql..Qh (1 ≤ Ql ≤ N; Ql ≤ Qh ≤ N)?The Hay Cow answers each of these queries with a single integer A whose truthfulness is not guaranteed.

Help the other cows determine if the answers given by the Hay Cow are self-consistent or if certain answers contradict others.

给一段长度为n,每个位置上的数都不同的序列a[1..n]和q和问答,每个问答是(x, y, r)代表RMQ(a, x, y) = 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: Ql, Qh, 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: 复制

20 4
1 10 7
5 19 7
3 12 8
11 15 12
输出样例#1: 复制

3
并查集+二分

题意:给出一些区间的最小值 求问 最早哪个问题会产生矛盾 输出

针对这个产生矛盾 我们可以二分判断 哪个地方最早出现矛盾

然后每次针对二分的一个值 我去判断一下是否成立 我首先针对权值排序

然后从大到小去覆盖 出现第一个无法覆盖的区域我就返回false

每次我是判断交集 覆盖并集 用并查集可以完成

#include<cstdio>
#include<algorithm>
#define Q 30000
#define N 1100000
#define inf 0x3f3f3f3f
using namespace std;
inline char gc(){
    static char now[1<<16],*T,*S;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0;char ch=gc();
    while (ch<'0'||ch>'9') ch=gc();
    while (ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=gc();}
    return x;
}
struct node{
    int l,r,v;
}data[Q],data1[Q];
inline bool cmp(node a,node b){
    if (a.v>b.v) return true;else if (a.v<b.v) return false;
    return a.l<b.l;
}
int fa[N],n;
inline int find(int x){
    return x==fa[x]?x:fa[x]=find(fa[x]);
}
inline bool check(int x){
    int l=0,r=inf;int l1=inf,r1=0;
    for (int i=1;i<=n+1;++i) fa[i]=i;
    for (int i=1;i<=x;++i) data[i]=data1[i];sort(data+1,data+x+1,cmp);
    int last_v=data[1].v;l=l1=data[1].l;r=r1=data[1].r; 
    for (int i=2;i<=x;++i){
        if (last_v!=data[i].v){
            int x=find(l);
            if(x<=r) {int now=find(l1);
                while (now<=r1) fa[now]=fa[now+1],now=find(now+1);
            }else return false;
            last_v=data[i].v;l=l1=data[i].l;r=r1=data[i].r;
        }else{
            l=max(l,data[i].l);r=min(r,data[i].r);l1=min(l1,data[i].l);r1=max(r1,data[i].r);
            if (l>r) return false;
        };
    }if (find(l)>r) return false;
    return true;
}
int l,r,q;
int main(){
    //freopen("bzoj1594.in","r",stdin);
    n=read();q=read();
    for (int i=1;i<=q;++i) data1[i].l=read(),data1[i].r=read(),data1[i].v=read();
    l=0;r=q;
    while (l<=r){
        int mid=l+r>>1;
        if (check(mid)) l=mid+1;else r=mid-1;
    }if (l>q)printf("0");else printf("%d",l);
    return 0;
}

线段树+二分

线段树也可以很好的解决这个问题 那就是可以每次nlogn重建线段树 但是显然动态加点线段树效果会更好 这里 其实这个题目是有个坑的 那就是 如果我认为整个区间的最小值是1e9那么因为我每个数都只会出现一次 所以其实这个询问也是不合法的所以我可以考虑每次贪心的把大的填入 然后看一下 我这次需要填入多少个 然后再判断下 大的数还能填多少 据此判断是否可行即可

#include<cstdio>
#include<algorithm>
#define Q 33000
#define N 1100000
using namespace std;
inline char gc(){
    static char now[1<<16],*T,*S;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0;char ch=gc();
    while (ch<'0'||ch>'9') ch=gc();
    while (ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=gc();}
    return x;
}
struct node{
    int l,r,v;
}data1[Q],data[Q];
struct node1{
    int l,r,left,right,v,lazy;
}tree[N<<2];
inline bool cmp(node a,node b){
    if (a.v>b.v) return true;else if (a.v<b.v) return false;
    return a.l<b.l;
}
int num,n,q,root;
inline void init(int &x){
    x=++num;tree[x].left=tree[x].right=tree[x].l=tree[x].r=tree[x].lazy=tree[x].v=0;
}
inline void pushdown(int x){
    if (!tree[x].lazy) return ;
    int mid=tree[x].l+tree[x].r>>1;
    if (!tree[x].left) init(tree[x].left);
    if (!tree[x].right) init(tree[x].right);
    int l=tree[x].left,r=tree[x].right;
    tree[l].l=tree[x].l;tree[l].r=mid;tree[r].l=mid+1;tree[r].r=tree[x].r;
    tree[l].v=mid-tree[x].l+1;tree[r].v=tree[x].r-mid;
    tree[l].lazy=tree[r].lazy=tree[x].lazy;
    tree[x].lazy=0;
} 
inline void update(int x){
    int l=tree[x].left,r=tree[x].right;
    tree[x].v=tree[l].v+tree[r].v;
}
void insert1(int &x,int l,int r,int l1,int r1){
    if(l>r) return; 
    if (!x) init(x);tree[x].l=l;tree[x].r=r;
    if (l1<=l&&r1>=r){tree[x].v=tree[x].r-tree[x].l+1;tree[x].lazy=1;return;}
    int mid=tree[x].l+tree[x].r>>1;pushdown(x);
    if (l1<=mid) insert1(tree[x].left,l,mid,l1,r1);
    if (r1>mid) insert1(tree[x].right,mid+1,r,l1,r1);
    update(x);
}
int query(int x,int l,int r){
    if (l>r) return 0;
    if (!x) return 0;
    if (l<=tree[x].l&&r>=tree[x].r) return tree[x].v;
    int mid=tree[x].l+tree[x].r>>1;pushdown(x);int tmp=0;
    if (l<=mid) tmp+=query(tree[x].left,l,r);
    if (r>mid) tmp+=query(tree[x].right,l,r);
    return tmp;
}
inline bool check(int x){
    for (int i=1;i<=x;++i) data[i]=data1[i];int tot=1e9;
    sort(data+1,data+x+1,cmp);num=0;int l,r,l1,r1;root=0;tree[0].l=tree[0].r=tree[0].left=tree[0].right=0;
    int last_v=data[1].v;l=l1=data[1].l;r=r1=data[1].r; int differ=0;
    for (int i=2;i<=x;++i){
        if (data[i].v!=last_v){
            if (query(root,l,r)==r-l+1) return false;int pre=tree[root].v;
            insert1(root,1,n,l1,r1);differ=tree[root].v-pre;
            if (tot-last_v+1<differ) return 0;tot-=differ;
            l=l1=data[i].l;r=r1=data[i].r;last_v=data[i].v;
        }else{
            l=max(l,data[i].l);r=min(r,data[i].r);l1=min(l1,data[i].l);r1=max(r1,data[i].r);
            if (l>r) return false;
        };

    }
    if (query(root,l,r)==r-l+1) return false;
    int pre=tree[root].v;
    insert1(root,1,n,l1,r1);differ=tree[root].v-pre;
    if (tot-last_v+1<differ) return 0;tot-=differ;
    return true;
}
int main(){
    freopen("bzoj1594.in","r",stdin);
    n=read();q=read();
    for (int i=1;i<=q;++i){data1[i].l=read();data1[i].r=read();data1[i].v=read();}
    int l=1,r=q;
    while (l<=r){
        int mid=l+r>>1;
        if (check(mid)) l=mid+1;else r=mid-1;
    }
    if (l>q) printf("0");else printf("%d",l);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值