1. 程式人生 > >【2018黑龍江省賽】UPC-7218 A Sequence Game(莫隊&離散化&RMQ ST表)

【2018黑龍江省賽】UPC-7218 A Sequence Game(莫隊&離散化&RMQ ST表)

題目描述
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=1; 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");
    }
}