HDU 6301 (2018多校第一场D)(双指针贪心)

传送门

题面:

Distinct Values

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 705    Accepted Submission(s): 186

Problem Description

Chiaki has an array of n positive integers. You are told some facts about the array: for every two elements ai and aj in the subarray al..r (l≤i<j≤r), ai≠ajholds.
Chiaki would like to find a lexicographically minimal array which meets the facts.

Input

There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:

The first line contains two integers n and m (1≤n,m≤105) -- the length of the array and the number of facts. Each of the next m lines contains two integers li and ri (1≤li≤ri≤n).

It is guaranteed that neither the sum of all n nor the sum of all m exceeds 106.

Output

For each test case, output n integers denoting the lexicographically minimal array. Integers should be separated by a single space, and no extra spaces are allowed at the end of lines.

Sample Input

3

2 1

1 2

4 2

1 2

3 4

5 2

1 3

2 4

Sample Output

1 2

1 2 1 2

1 2 3 1 1

Source

2018 Multi-University Training Contest 1

题目描述:

    给你一个大小为n的数列,有m种要求,要求在数列的[l,r]区间内的数是不相同的,问你原数列是什么,并要求原数列的字典序最小。

题目分析:

    首先对于这个题目,我们需要贪心的去想。因为要保证字典序最小,因此我们首先应该考虑到如果在一个连续的区间[1,r]内,则该数列的最优的方案必定是从1一直填补到r,最后其他不在要求的区间内的位数均置为0。

    而倘若有两个区间出现重合,我们会发现,在第一个区间遍历结束后,第一个区间的左端点到第二个区间的左端点之间的数必定可以放置到第一个区间的右端点到第二个区间的有端点处(因为之间的数必定最小,因此答案必定最优)。而倘若第二个区间还未访问完,则继续按照数的大小填数。

    exp:如果对于一个1到6的数组,第一个区间为[1,4],第二个区间为[3,6],则不难发现最优解一定为[1,2,3,4,1,2]。

    了解到上述的贪心思路,我们就可以进行实现。

    我们可以建立一个数组L[i]代表当前第i位的最左边的边界,建立一个数组R[i]代表当前第i位的最右边的边界。因为在取数的过程中,我们需要不断的维护一个递增的数列,因此我们可以考虑用一个优先队列进行维护。并且用双指针去维护哪些位置的数可以重新进入队列即可。

代码:

#include <bits/stdc++.h>
#define maxn 100005
using namespace std;
int l[maxn];//第i位的最左边的点在哪
int r[maxn];//第i位的最右边的点在哪
int vis[maxn];//标记
int ans[maxn];//答案
const int INF=0x3f3f3f3f;
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m;
        priority_queue<int,vector<int>,greater<int> >que;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){//初始化
            ans[i]=1;
            vis[i]=1;
            l[i]=n;
            r[i]=i;
            que.push(i);
        }
        int Lmost=INF;
        for(int i=0;i<m;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            l[y]=min(l[y],x);//更新数组
            r[x]=max(r[x],y);
            Lmost=min(Lmost,x);//更新左指针
        }
        int R=Lmost;//更新右指针
        for(int i=Lmost;i<=n;i++){//枚举所有的点
            while(r[R]<i&&R<i) R++;//如果前一个边界结束了,右指针扩展
            for(int j=Lmost;j<R;j++){//将之前的不在区间交集的数重新入队
                if(!vis[ans[j]]){
                    que.push(ans[j]);
                    vis[ans[j]]=1;
                }
            }
            Lmost=R;//更新左指针
            ans[i]=que.top();
            vis[ans[i]]=0;
            que.pop();
        }
        for(int i=1;i<n;i++){
            cout<<ans[i]<<" ";
        }
        cout<<ans[n]<<endl;
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_39453270/article/details/81174981