杭电多校-Loop-(不确定性贪心+线段树)

Loop

题意:
就是给你一个数组,然后你可以操作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;
}

总结:
多多思考,注意细节。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值