【题目】
题目描述:
在看了 jiry_2 的课件《Segment Tree Beats!》后,小 O 深深沉迷于这种能单次 O(log n) 支持区间与一个数取 min/max,查询区间和等信息的数据结构,于是他决定做一道与区间与一个数取 min/max 的好题。
这题是这样的:你得到了一个长度为 n n n 的数列 a i {a_i} ai,要求支持以下 2 种操作:第一种是给定 L,R,X,要求把区间中比 X 小的数字全部修改为 X;第二种是给定 L,R,K,X,查询区间中比 X 小的最小的 K 个数,并且将它们升序输出,没有则输出 -1。
小 O 觉得这题太简单了,于是把这题丢给了你,请你帮忙实现。
下发文件中有 jiry_2 的课件《Segment Tree Beats!》,不保证其与解题有关。
输入格式:
第一行一个数字
n
n
n 表示数列长度,
第二行
n
n
n 个数字分别表示
a
1
a_1
a1…
a
n
a_n
an,
第三行一个数字
m
m
m 表示操作次数,
接下来
m
m
m 行每行表示一次操作,
第一个数
o
p
op
op 表示操作类型,
o
p
op
op 可能是
1
1
1 或
2
2
2,
如果
o
p
=
1
op=1
op=1,后面有 L,R,X 三个正整数表示把区间 [L,R] 中比 X 小的数字全部改成 X
如果
o
p
=
2
op=2
op=2,后面有 L,R,X,K 四个正整数表示查询区间 [L,R] 中比 X 小的最小的 K 个数
输出格式:
对于每个
o
p
=
2
op=2
op=2,输出一行,
如果比 X 小的数达到了 K 个,升序输出最小的 K 个数,
如果比 X 小的数小于 K 个,输出一行一个 -1 即可.
样例数据:
输入
3
1 2 3
4
1 1 2 2
2 1 3 1 3
2 1 3 2 1
2 1 3 3 2
输出
-1
-1
2 2
备注:
本题共 6 个测试点,不采用 subtask 评测,但每个测试点分值不同。
对于全部数据,满足 1<=n,m<=500000,1<=L<=R<=n,1<=K<=n,1<=Ai,X<=10^9,对于所有操作 2 中的 K,K 的总和不超过 5*10^6。
~1:12pts,满足 1<=n,m<=3000;
~2:7pts,满足 1<=n,m<=100000,没有操作 1,且对于所有操作 2 有 K=1;
~3:23pts,满足 1<=n,m<=100000,对于所有操作 2 有 K=1;
~4:37pts,满足 1<=n,m<=100000,没有操作 1;
~5:6pts,满足 1<=n,m<=100000;
~6:15pts,无特殊限制。
【分析】
这道题有一种偏暴力的算法
首先对于操作 1,直接修改区间最小值,若 Min<X,则将 Min 改为 X;否则就不变
对于操作 2,操作 K 次,每次都找出那个区间的最小值 Min,然后判断 Min 和 X 的关系(即若 Min>=X 的话就该输出 -1),然后将那个位置上的数改为 inf(保证下一次找最小值的时候不会找到它),做完这 K 次操作后再把原来的值改回来
由于 K 的总和不超过 5*10^6,这样做复杂度是有保证的(但是常数有点大),在代码中加点读优、输优之类的还是能 A 掉这道题的(为了代码简洁我就没加了)
【代码】
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#define N 500005
#define inf (1ll<<31ll)-1
using namespace std;
int a[N],tag[N<<2];
pair<int,int>minn[N<<2];
inline void build(int root,int l,int r)
{
if(l==r)
{
minn[root]=make_pair(a[l],l);
return;
}
int mid=(l+r)>>1;
build(root<<1,l,mid);
build(root<<1|1,mid+1,r);
minn[root]=min(minn[root<<1],minn[root<<1|1]);
}
inline void change(int root,int k)
{
minn[root].first=max(minn[root].first,k);
tag[root]=max(tag[root],k);
}
inline void pushdown(int root)
{
change(root<<1,tag[root]);
change(root<<1|1,tag[root]);
tag[root]=0;
}
inline void modify(int root,int l,int r,int x,int y,int k)
{
if(l>=x&&r<=y)
{
change(root,k);
return;
}
int mid=(l+r)>>1;
if(tag[root]) pushdown(root);
if(x<=mid) modify(root<<1,l,mid,x,y,k);
if(y>mid) modify(root<<1|1,mid+1,r,x,y,k);
minn[root]=min(minn[root<<1],minn[root<<1|1]);
}
inline pair<int,int> ask(int root,int l,int r,int x,int y)
{
if(l>=x&&r<=y)
return minn[root];
int mid=(l+r)>>1;
if(tag[root]) pushdown(root);
pair<int,int>ans;ans.first=inf;
if(x<=mid) ans=min(ans,ask(root<<1,l,mid,x,y));
if(y>mid) ans=min(ans,ask(root<<1|1,mid+1,r,x,y));
return ans;
}
inline void insert(int root,int l,int r,int p,int x)
{
if(l==r)
{
minn[root]=make_pair(x,p);
return;
}
int mid=(l+r)>>1;
if(tag[root]) pushdown(root);
if(p<=mid) insert(root<<1,l,mid,p,x);
else insert(root<<1|1,mid+1,r,p,x);
minn[root]=min(minn[root<<1],minn[root<<1|1]);
}
int main()
{
// freopen("segtree.in","r",stdin);
// freopen("segtree.out","w",stdout);
int n,m,i,j;
scanf("%d",&n);
for(i=1;i<=n;++i)
scanf("%d",&a[i]);
build(1,1,n);
scanf("%d",&m);
int s,l,r,x,k;
for(i=1;i<=m;++i)
{
scanf("%d%d%d%d",&s,&l,&r,&x);
if(s==1) modify(1,1,n,l,r,x);
else
{
scanf("%d",&k);
bool flag=true;
vector<pair<int,int> >ans;
for(j=1;j<=k;++j)
{
pair<int,int>Min=ask(1,1,n,l,r);
if(Min.first>=x) {flag=false;break;}
ans.push_back(Min);
insert(1,1,n,Min.second,inf);
}
if(!flag) printf("-1\n");
else
{
for(j=0;j<ans.size();++j)
printf("%d ",ans[j].first);
printf("\n");
}
for(j=0;j<ans.size();++j)
insert(1,1,n,ans[j].second,ans[j].first);
}
}
// fclose(stdin);
// fclose(stdout);
return 0;
}