题意:给你一个区间
,m个询问,每次询问
,表示这段区间数字不相重,输出字典序最小的排列.
分析:
区间与区间的关系有三种:包含,相交,分离;
把包含区间段的可以去掉,只剩下两种。先按照左端排下序,用线段树维护填第i个位置的最小值,查询
,区间单点更新
.
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long LL;
const int MAXN = 1e5 + 10;
int dp[MAXN << 2], cnt[MAXN];
struct node {
int L, R;
}s[MAXN], ss[MAXN];
void pushup(int root) {
dp[root] = min(dp[root << 1], dp[root << 1 | 1]);
}
void build(int root, int L, int R) {
if(L == R) {
dp[root] = L;
return ;
}
int mid = (L + R) >> 1;
build(root << 1, L, mid);
build(root << 1 | 1, mid + 1, R);
pushup(root);
}
void updata(int root, int L, int R, int x, int flag) {
if(L == R) {
if(flag) dp[root] = L;
else dp[root] = 1e8;
return ;
}
int mid = (L + R) >> 1;
if(x <= mid) updata(root << 1, L, mid, x, flag);
else updata(root << 1 | 1, mid + 1, R, x, flag);
pushup(root);
}
bool cmp(node x, node y) {
if(x.L != y.L) return x.L < y.L;
return x.R > y.R;
}
int main() {
int T, n, m;
while(scanf("%d", &T) != EOF) {
while(T--) {
memset(cnt, 0, sizeof(cnt));
scanf("%d %d", &n, &m);
build(1, 1, n);
for(int i = 0; i < m; ++i) {
scanf("%d %d", &ss[i].L, &ss[i].R);
}
sort(ss, ss + m, cmp);
int p = 1, ans = ss[0].R;
s[0].L = ss[0].L, s[0].R = ss[0].R;
for(int i = 1; i < m; ++i) {
if(ans >= ss[i].R) continue;
ans = ss[i].R;
s[p].L = ss[i].L, s[p++].R = ss[i].R;
}
int res = s[0].L;
for(int i = 0; i < p; ++i) {
res = max(s[i].L, res);
for(int j = res; j <= s[i].R; ++j) {
cnt[j] = dp[1];
updata(1, 1, n, cnt[j], 0);
}
res = s[i].R + 1;
if(i == p - 1) continue;
int len = min(res, s[i + 1].L);
for(int j = s[i].L; j < len; ++j) {
updata(1, 1, n, cnt[j], 1);
}
}
for(int i = 1; i < n; ++i) {
if(!cnt[i]) printf("%d ", 1);
else printf("%d ", cnt[i]);
}
printf("%d\n", cnt[n] ? cnt[n] : 1);
}
}
return 0;
}