贪心+最优策略+区间更新 7.23杭电多校赛 D

4.

Distinct Values

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

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

题目大意:

​ 给有n个数的数组,m个区间,问对所有区间都满足"区间内的数字不相同"条件的变化后的数组。输出字典序最小的那个。

想法:

​ 字典序最小,那肯定是贪心从小到大填数了。关键是区间之间的关系:相交、包含,怎么处理。

思路:

​ 1、pre[区间右端点]=区间左端点。大区间内不重复,就可以保证被包含的小区间不重复,所以就不用记录小区间了。第一遍输入,pre[区间右端点]=最小的左端点,去除小区间的左端点。第二遍刷,pre[i]=min(pre[i],pre[i+1]);去除小区间的右端点。这样解决了大部分包含的情况。

​ 2、贪心,set保存可用数集。不重复区间内从可用数集中递增地挑数填,并删掉被填了的可用数。

​ 3、pl记录上个不重复区间的左端点,到达下一个记录着不重复区间左端点pre[i]的右端点位置i时,将pl和i之间的数都放入可用数集。这样解决了相交的情况。

标准题解

显然可以从左往右贪心,问题转化成求一个区间里的mex,由于区间左右端点都是递增的,用个set维护下即可。

AC代码:

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const ll mod=1000000007;
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
// head
​
const int N=101000;
int _,n,m,pre[N],l,r,ret[N];
int main() {
    for (scanf("%d",&_);_;_--) {
        scanf("%d%d",&n,&m);
        rep(i,1,n+1) pre[i]=i;                  //pre[i]存i前不能重复的区间左端点 
        rep(i,0,m) {
            scanf("%d%d",&l,&r);
            pre[r]=min(pre[r],l);
        }
        per(i,1,n) pre[i]=min(pre[i],pre[i+1]);     //更新被包含的小区间到大区间     
        int pl=1;                                   //记录不重复区间的左端点 
        set<int> val;
        rep(i,1,n+1) val.insert(i);
        rep(i,1,n+1) {
            while (pl<pre[i]) {         //到达不重复区间右端点 
                val.insert(ret[pl]);    //将之前使用过的值填回可用值的set里 
                pl++;                   //区间左端点右移 
            }
            ret[i]=*val.begin();   //取set里最小值填该位
            val.erase(ret[i]);      //该值之后就不能用了,删掉 
        }
        rep(i,1,n+1) printf("%d%c",ret[i]," \n"[i==n]);
    }
}

参考:

https://www.ideone.com/Wo55gi

猜你喜欢

转载自blog.csdn.net/KeeeepGO/article/details/81187257
今日推荐