【2018黑龙江省赛】UPC-7218 A Sequence Game(莫队&离散化&RMQ ST表)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/kuronekonano/article/details/82821538

题目描述
One day, WNJXYK found a very hard problem on an Online Judge. This problem is so hard that he had been thinking about the solutions for a couple of days. And then he had a surprise that he misunderstood that problem and easily figured out a solution using segment tree. Now he still wonders that solution for the misread problem.
There is a sequence with N positive integers A1,A2,…,An and M queries. Each query will give you an interval [L,R] and require an answer with YES / NO indicates that whether the numbers in this interval are continuous in its integer range.
Let us assume that the maximal number in an interval is mx and the minimal number is mi. The numbers in this interval are continuous in its integer range means that each number from mi to mx appears at least once in this interval.

输入
The input starts with one line contains exactly one positive integer T which is the number of test cases. And then there are T cases follow.
The first line contains two positive integers n,m which has been explained above.
The second line contains n positive integers A1,A2,…,An.
Then there will be m lines followed. Each line contains to positive numbers Li,Ri indicating that the i th query’s interval is [Li,Ri].

输出
For each test case, output m line.
Each of following m lines contains a single string “YES”/ “NO” which is the answer you have got.

样例输入
2
3 3
3 1 2
2 3
1 3
1 2
5 3
1 2 2 4 5
1 5
1 3
3 3

样例输出
YES
YES
NO
NO
YES
YES

提示
T=5
1≤n≤100000
1≤Ai≤10^9
1≤m≤100000
The input file is very large, so you are recommend to use scanf() and printf() for IO.
题意: 给出一个长度为n的序列a,其中ai值小于等于1e9,有m次询问,每次询问给出一个区间[L,R],询问该区间内的最大值和最小值之间的数是否都至少出现过一次。

题解: 首先是查找给出区间查找区间内最大值和最小值,这个用RMQ处理即可,然后是如何处理某个区间内出现的数的是否在最大最小值范围内,且一个不落,可以知道,统计一个区间内不同的数的个数,并且知道这个区间内最大值和最小值,就可以知道该区间内是否存在足够的数铺满最大最小值之间的数,因为区间大小已知,该区间内值不会超过或低于最大最小值,也就是说出现的值一定在范围内,然后不同的数的个数限定了是每个数只能至少出现一次,如果出现多次,除非该区间长度大于最大值减最小值,否则必须是每个数都出现过。用莫队算法维护即可。每次移动两个指针,计算每个值对该区间出现的不同数的数量的影响。
因为值特别大,因此要做离散化,然后映射到标记数组中标记出现次数,出区间的值出现次数减少,减到0时不同数的数量减1,进区间的值,如果是第一次出现,不同数的数量加1,用于计算每个区间的结果。

#include<bits/stdc++.h>
#define LL long long
#define M(a,b) memset(a,b,sizeof a)
#define pb(x) push_back(x)
using namespace std;
const int maxn=1e5+7;
int dmin[maxn][18];
int dmax[maxn][18];
int cnt[maxn];
int pos[maxn],id[maxn];
bool ans[maxn];
int n,m,t,sum;
vector<int>a,b;
struct node
{
    int l,r,id;
    bool operator <(const node &a)const
    {
        return pos[l]==pos[a.l]?r<a.r:pos[l]<pos[a.l];
    }
} q[maxn];
int getid(int x)
{
    return lower_bound(a.begin(),a.end(),x)-a.begin()+1;
}
int rmq(int l,int r)
{
    int k=log2(r-l+1);
    int minn= min(dmin[l][k],dmin[r-(1<<k)+1][k]);
    int maxx= max(dmax[l][k],dmax[r-(1<<k)+1][k]);
    return maxx-minn+1;
}
void add(int x)
{
    if(cnt[id[x]]==0)sum++;
    cnt[id[x]]++;
}
void del(int x)
{
    cnt[id[x]]--;
    if(cnt[id[x]]==0)sum--;
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        M(cnt,0);
        a.clear();
        b.clear();
        scanf("%d%d",&n,&m);
        int sz=sqrt(n);
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&dmax[i][0]);
            dmin[i][0]=dmax[i][0];
            a.pb(dmax[i][0]);
            pos[i]=i/sz;
        }
        b=a;
        sort(a.begin(),a.end());
        a.erase(unique(a.begin(),a.end()),a.end());
        for(int i=1;i<=n;i++)id[i]=getid(b[i-1]);
        for(int j=1; (1<<j)<n; j++)
            for(int i=0; i+(1<<j)-1<=n; i++)
            {
                dmin[i][j]=min(dmin[i][j-1],dmin[i+(1<<(j-1))][j-1]);
                dmax[i][j]=max(dmax[i][j-1],dmax[i+(1<<(j-1))][j-1]);
            }
        for(int i=1; i<=m; i++) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
        sort(q+1,q+1+m);
        int L=1,R=0;
        sum=0;
        for(int i=1;i<=m;i++)
        {
            while(L<q[i].l) del(L++);
            while(L>q[i].l) add(--L);
            while(R<q[i].r) add(++R);
            while(R>q[i].r) del(R--);
            ans[q[i].id]= rmq(q[i].l,q[i].r)==sum?true:false;
        }
        for(int i=1;i<=m;i++)printf("%s\n",ans[i]?"YES":"NO");
    }
}

猜你喜欢

转载自blog.csdn.net/kuronekonano/article/details/82821538