TCO14 Round 2C InverseRMQ

题目描述
RMQ问题即区间最值问题是一个有趣的问题。

在这个问题中,对于一个长度为 n 的排列,query(l,r) 将返回 al,⋯,ar 中的最大值。

如对于 {3,1,4,2,5},query(2,4)=max(1,4,2)=4

现在我们给出 m 次询问的结果,问是否存在至少一个长度为 n 的排列 P 满足所有的条件。

输入格式
第一行 T

每一组数据中,第一行 n,m,接下来 m 行,每行 li,ri,ans

输出格式
T 行 Possible 或 Impossible

数据范围
对于 20% 的数据, n≤10

对于另 10% 的数据,li=ri

对于另 20% 的数据,[li,ri] 两两没有交集

对于另 20% 的数据,ansi 互不相同

对于所有数据,T≤10,n,m≤2000,1≤li≤ri≤n

时间限制 1s

空间限制 256MB


这道题的做法我也不知道叫什么,大概是一种构造的思路吧。
我们考虑如何能构造出满足要求的序列:
首先,我们按照所有的区间的最大值的大小排序,小的在前。那么如果有两段区间 [l1,r1] [ l 1 , r 1 ] [l2,r2] [ l 2 , r 2 ] ,如果 num1<num2 n u m 1 < n u m 2 l1<l2,r1>l2 l 1 < l 2 , r 1 > l 2 (也就是区间1和2相交),那么显然区间2和区间 [r1,r2] [ r 1 , r 2 ] 是等价的,因为在区间2的左半部分(也就是 [l2,r1] [ l 2 , r 1 ] )所有的数都小于 num1 n u m 1 ,所以不可能等于 num2 n u m 2 。既然如此,我们就 N2 N 2 枚举求出所有区间经过上述操作后的区间。(上述操作指的就是割去一个 num n u m 值比它小并且与它相交的区间与它相交的部分。)
显然如果最后操作完该区间已经不存在了,那么就输出Impossible。
比如区间 [2,7] [ 2 , 7 ] num n u m 为6,而区间 [1,5],[4,7] [ 1 , 5 ] , [ 4 , 7 ] 的num都为5,那么就输出Impossible,因为区间 [2,7] [ 2 , 7 ] 被割完之后就没有剩余的部分了,所有的位置都被小于num的数占据,没有位置放num。
然后还有一种情况没有考虑,就是如果存在两个区间,他们的num值相等,那么放num的地方就是这两个区间相交的地方。如果这两个区间不相交,直接输出Impossible。于是我们将num相同的区间求交集后,得到的区间就是最后的区间。但是实际上所有num相同的区间构成的并集(出去交集)的部分也是有限制的,就是他们的最大值只能是num-1。所以我们还要加上这段区间。(我们将这些并集的区间进行特定的标记,因为他们的操作不同)
通过上述的操作,我们得到了一些区间。并且已经是从小到大排列好的。
那么我们就从每个区间构造这个数,由于区间的num值是递增的,所以在统计答案的时候这个区间最大值小于num其实也无所谓,我们就直接贪心的从小到大取连续的一块即可(这样虽然不能构造出正确的序列,但是对序列的性质并无影响)。
上面的做法是非常显然的,因为对于num值为x的区间来说,它之前的区间的num值肯定小于x,那么在小于x的数里任选n个,最终剩下的数的个数都是一样的。而对于判断Possible来说,只关心剩下的数的个数。如果剩下的数的个数小于该段区间的长度,那么就输出Impossible。
这道题确实是细节题,还有许多的细节在代码里讲。
#include<bits/stdc++.h>
#define MAXN 2005
using namespace std;
int read(){
    char c;int x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
    while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
int T,n,m,top,tot,flag,vis[2005][2005];
struct node{
    int l,r,num,bar;
}F[MAXN],Q[MAXN];
int cmp(node a,node b){
    return a.num<b.num;
}
int intersect(node a,node b){  //判断区间是否相交
    if(a.r>=b.l&&a.l<=b.l) return 1;
    if(a.r>=b.r&&a.l<=b.r) return 1;
    if(a.r<=b.r&&a.l>=b.l) return 1;
    return 0;
}
void paint(int x,int y){  //割除num小于其的并与其相交的区间
    for(int i=max(Q[x].l,Q[y].l);i<=min(Q[x].r,Q[y].r);i++){
        vis[x][i]=1;
    }
}
int all(int x){  //统计该区间的长度
    int res=0;
    for(int i=Q[x].l;i<=Q[x].r;i++)
     if(!vis[x][i]) res++;
    return res;
}
int main()
{
    T=read(); 
    while(T--){
        memset(Q,0,sizeof(Q));
        memset(F,0,sizeof(F));
        memset(vis,0,sizeof(vis));
        n=read();m=read();flag=0;tot=0;top=0;
        for(int i=1;i<=m;i++){
            F[i]=(node){read(),read(),read()};
            if(F[i].num<F[i].r-F[i].l+1){  //如果刚开始的num小于区间的长度,那肯定是impossible
                puts("Impossible");flag=1;
            }
            if(F[i].num>n){
                puts("Impossible");flag=1;
            }
        }
        if(flag) continue;
        sort(F+1,F+1+m,cmp);
        int i=1;
        if(flag) continue;
        while(i<=m){
            int ml=F[i].l,mr=F[i].r,xl=F[i].l,xr=F[i].r;
            for(int j=i+1;j<=m+1;j++){
                if(F[j].num!=F[i].num){  //统计新的区间
                    if(ml<xl) Q[++top]=(node){ml,xl-1,F[i].num-1,1};
                    if(mr>xr) Q[++top]=(node){xr+1,mr,F[i].num-1,1};
                    Q[++top]=(node){xl,xr,F[i].num,0};
                    i=j;break;
                }
                else{
                    ml=min(ml,F[j].l);mr=max(mr,F[j].r);  //求并集
                    xl=max(xl,F[j].l);xr=min(xr,F[j].r);  //求交集
                    if(xl>xr){  //交集为空,则impossible
                        puts("Impossible");flag=1;break;
                    }
                }
            }
            if(flag) break;
        }
        if(flag) continue;
        for(int i=1;i<=top;i++)
         for(int j=i+1;j<=top;j++){  //割除区间
            if(intersect(Q[i],Q[j])){
                if(Q[i].num<Q[j].num||Q[j].bar)paint(j,i);
            }
         }
        for(int i=1;i<=top;i++){  // bar为1的区间要特判(也就是并集的区间),如果这段区间的长度为0,那是可以的,代表这段区间的值全小于等于交集的(num-1)
            int pj=all(i);
            if(!pj&&Q[i].bar) continue;
            if(!pj||Q[i].num-tot<pj){ //如果没地方放,或者能放的数小于需要放的地方,则impossible
                flag=1;puts("Impossible");break;
            }
            tot+=pj;
        }
        if(flag) continue;
        puts("Possible");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值