题意:
就是给你一个数组,然后你可以操作k次,每次可以选择两个数l和r,让va[l]插到va[r]的后面。然后问你操作k次后,这个数组的字典序最大是多少。
思考:
1.每次可以让一个数插到后面,然后让你求字典序最大,那么我肯定保证最前面的能多大就多大。所以第一个可以从[1,k+1]这段区间取一个最大值成为最前面的数,因为你可以把前面的k个都扔到后面。所以每次我就可以把某段区间的最大值当成前面的,这个最大值前面的数都扔到后面,扔到哪里先不管。
2.对于扔掉点我都放在一个序列里,然后就一直从前往后处理,直到处理到最后一个数。现在得到两个序列,一个是每次固定的最大值的序列,一个是我扔掉的数的序列。然后我就可以从前往后遍历最大值的序列,如果我扔掉的序列的数大于当前遍历的数,那么我就可以先放这个仍的数,因为每次我都往后仍嘛,我就让它扔在这。
3.但是当时我思考,会不会它不应该仍在这的呢?应该仍在靠前的地方,其实不会,因为第一次你去的是[1,k+1]的最大值,那么这个你仍的数肯定不会出现在前面。所以不用担心这个的。
4.对于每次你可以在[i,i+k-1]这段区间找最大值,如果每次暴力找超时了,我可以先用线段树维护出最大值。然后维护n个set,每个set是每个数字出现的位置。我就可以在s[maxn]找到第一个>=i的位置,为啥是第一个,因为我处理第一个就够了,剩下的k留给后面的。这样就找到了最大值的位置。牛客练习赛85-哲学家的沉思,这个题目也是用的这种操作。
5.对于输出答案的时候,如果固定序列的当前值>=扔掉序列的最大值,我是先输出谁呢?要先输出当前固定序列的值,因为扔掉的数最大值就那么大了,但是固定序列的最大值后面可能还会更大呢,所以这里是个细节。
6.还有一点就是,当k非常大的时候,如果都用不k,其实用管,我就选l==r操作后等于没有操作。如果r必须>l。那么就要判断答案数组里面是否有相邻的相同值,让他俩换,如果没有,我就换最后两个直到k为0。
代码:
#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define db double
#define int long long
#define PII pair<int,int >
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define node_l node<<1
#define node_r node<<1|1
using namespace std;
const int mod = 1e9+7,inf = 1e18;
const int N = 1e6+10,M = 2010;
int T,n,m,k;
int va[N];
int vb[N];
int vc[N];
int cnt1,cnt2;
set<int > s[N];
struct seg_max{
struct node{
int L,R;
int maxn;
}t[4*N];
void pushup(int node)
{
t[node].maxn = max(t[node_l].maxn,t[node_r].maxn);
}
void build(int node,int l,int r)
{
t[node].L = l,t[node].R = r;
t[node].maxn = -inf; //多次建树初始化细节,虽然会被pushup更新,但是有时候没有pushup的时候,就会错,所以就都初始化掉就可以了
if(l==r)
{
t[node].maxn = va[l];
return ;
}
int mid = (l+r)>>1;
build(node_l,l,mid);build(node_r,mid+1,r);
pushup(node);
}
void update(int node,int l,int r,int value)
{
if(t[node].L>=l&&t[node].R<=r)
{
t[node].maxn = max(t[node].maxn,value);
return ;
}
int mid = (t[node].L+t[node].R)>>1;
if(r<=mid) update(node_l,l,r,value);
else if(l>mid) update(node_r,l,r,value);
else update(node_l,l,mid,value),update(node_r,mid+1,r,value);
pushup(node);
}
int query(int node,int l,int r)
{
if(t[node].L>=l&&t[node].R<=r) return t[node].maxn;
int mid = (t[node].L+t[node].R)>>1;
if(r<=mid) return query(node_l,l,r);
else if(l>mid) return query(node_r,l,r);
else return max(query(node_l,l,mid),query(node_r,mid+1,r));
pushup(node);
}
}t_max;
void solve()
{
cnt1 = 0,cnt2 = 0;
for(int i=1;i<=n;i++)
{
int l = i,r = min(n,i+m);
int maxn = t_max.query(1,l,r); //这段区间的最大值
int idx = *s[maxn].lower_bound(l); //第一个出现的位置
vb[++cnt1] = va[idx];
for(int j=i;j<idx;j++) vc[++cnt2] = va[j];
m -= idx-i;
i = idx;
}
sort(vc+1,vc+1+cnt2,greater<int>());
vector<int > anw;
int i = 1,j = 1;
while(i<=cnt1||j<=cnt2)
{
if(i>cnt1)
{
anw.pb(vc[j++]);
continue;
}
if(j>cnt2)
{
anw.pb(vb[i++]);
continue;
}
if(vb[i]>=vc[j]) anw.pb(vb[i++]); //还是先拿固定序列的
else anw.pb(vc[j++]);
}
for(int i=0;i<anw.size();i++)
{
cout<<anw[i];
if(i!=anw.size()-1) cout<<" ";
}
cout<<"\n";
}
signed main()
{
IOS;
cin>>T;
while(T--)
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>va[i];
t_max.build(1,1,n);
for(int i=1;i<=t_max.query(1,1,n);i++) s[i].clear(); //初始化
for(int i=1;i<=n;i++) s[va[i]].insert(i);
solve();
}
return 0;
}
总结:
多多思考,注意细节。