[UNR #6]稳健型选手

稳健型选手

题解

我是废物
首先看到这题,先考虑我们 Q A Q \rm QAQ QAQ蚤的最优策略是什么。
显然,原题的选择问题相当于可以抽象转化一下, ∀ i ∈ [ 1 , n 2 ] \forall i\in[1,\frac{n}{2}] i[1,2n],前 2 i 2i 2i个中最多选择 i i i个。
这个还是比较好证明的,因为当你选了 i i i个后,另外一个人必然在 [ 1 , 2 i ] [1,2i] [1,2i]中选了 i i i个,这样你在这个区间内最多也只能选 i i i个了。
这样我要贪心选的话显然是考虑从后往起那算,每次选择当前能选择的后缀中最大的一个数,这显然是正确的。
这样的话我们也就能够 O ( n log ⁡ n ) O\left(n\log n\right) O(nlogn)地处理一个长为 n n n的序列到了,那么在面对多次询问时我们又能有什么优化方法呢?

不妨考虑回滚莫队,在已经处理了一个完整的前缀的答案后,如果我们现在要在这个串加入不超过 B B B的点,会对答案产生怎样的影响。
显然,这 B B B个点本身是会产生 B 2 \frac{B}{2} 2B个能新加入的点的。我们可以先将这些点用上面的方法先算出来,然后剩下了一些点是在后面不能加入的,但它们还是可以替换掉前面已经加入的点。
我们可以把前面最小的 B 2 \frac{B}{2} 2B个已经加入的点拿出来,跟后面尚未加入的这 B 2 \frac{B}{2} 2B个点归并,从而得到哪些前面的点会被后面的点替换掉。
这样的话,我们只需要对前面维护一个已经加入节点的权值线段树即可。
时间复杂度也就被优化到了 O ( n n log ⁡ n ) O\left(n\sqrt{n}\log n\right) O(nn logn)
如果你把后面这部分每个后缀所对应的尚未加入的点也利用线段树预处理出来的话,实际上是可以达到 O ( n n log ⁡ n ) O\left(n\sqrt{n\log n}\right) O(nnlogn ),不过好像都过不了 n ⩽ 2 × 1 0 5 n\leqslant 2\times 10^5 n2×105

尝试继续优化。
显然,上面算法的重点是在合并两个已经做好选择的串。
如果在串 A A A后面接上串 B B B的话,相当于是让串 B B B中一些没能被选择的点优化掉串 A A A中的一些被选择了的点。
很明显,如果两个东西我们都是利用线段树维护的话,完全可以在线段树上二分求解。
只要找到最大的后缀,使得两棵权值线段树上这个后缀的数的数量加起来不超过原来选择的数的数量即可,这东西好搞。
所以我们完全可以尝试利用分治的方法解决这个问题。
对于 [ l , m i d ] [l,mid] [l,mid]中的部分,可以利用可持久化线段树处理出来对于每个左端点,到 m i d mid mid为止的串里哪些点被选了。
同样,对于 ( m i d , r ] (mid,r] (mid,r]的部分,也可以通过类似的方法求出哪些点没有被选。
那么跨越 m i d mid mid的询问,就能够直接把两边的线段树拿出来一起二分,求出哪些点会被替代,再加上先预处理的没别影响的点的总和就能得到答案了。
显然,这样每个位置都最多只会被加入 log ⁡ n \log n logn次,大大优化了时间复杂度。

总时间复杂度 O ( n log ⁡ 2 n ) O\left(n\log^2 n\right) O(nlog2n)

源码

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int,LL> pii;
#define MAXN 200005
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lowbit(x) (x&-x)
const int mo=998244353;
const int inv2=5e8+4;
const int jzm=2333;
const int zero=15;
const int INF=0x3f3f3f3f;
const double Pi=acos(-1.0);
const double eps=1e-9;
const int lim=1000000;
const int orG=3,ivG=332748118;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
    _T f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
    x*=f;
}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;}return t;}
int n,q,a[MAXN],root[MAXN],ord[MAXN],p[MAXN];
LL summ[MAXN],ans[MAXN];
struct ming{int l,r;}s[MAXN];
struct node{int lson,rson,num;LL sum;};
vector<int>vec[MAXN<<2];
class SegmentTree{
    private:
        node tr[MAXN*40];int tot;
    public:
        void insert(int &now,int las,int l,int r,int ai,int aw){
            if(l>r||l>ai||r<ai)return ;tr[now=++tot]=tr[las];
            tr[now].num+=aw;tr[now].sum+=aw*a[ord[ai]];
            if(l==r)return ;int mid=l+r>>1;
            if(ai<=mid)insert(tr[now].lson,tr[las].lson,l,mid,ai,aw);
            if(ai>mid)insert(tr[now].rson,tr[las].rson,mid+1,r,ai,aw);
        }
        int queryL(int rt,int l,int r,int k){
            if(l==r)return l;int mid=l+r>>1;
            if(tr[tr[rt].lson].num>=k)return queryL(tr[rt].lson,l,mid,k);
            return queryL(tr[rt].rson,mid+1,r,k-tr[tr[rt].lson].num);
        }
        int queryR(int rt,int l,int r,int k){
            if(l==r)return l;int mid=l+r>>1;
            if(tr[tr[rt].rson].num>=k)return queryR(tr[rt].rson,mid+1,r,k);
            return queryR(tr[rt].lson,l,mid,k-tr[tr[rt].rson].num);
        }
        int ask(int x,int y,int l,int r,int k){
            if(l==r)return l;int mid=l+r>>1;
            int tmp=tr[tr[x].rson].num+tr[tr[y].rson].num;
            if(tmp>=k)return ask(tr[x].rson,tr[y].rson,mid+1,r,k);
            return ask(tr[x].lson,tr[y].lson,l,mid,k-tmp);
        }
        LL askSum(int rt,int l,int r,int al,int ar){
            if(l>r||l>ar||r<al||al>ar||!rt)return 0;
            if(al<=l&&r<=ar)return tr[rt].sum;
            int mid=l+r>>1;LL res=0;
            if(al<=mid)res+=askSum(tr[rt].lson,l,mid,al,ar);
            if(ar>mid)res+=askSum(tr[rt].rson,mid+1,r,al,ar);
            return res;
        }
        void clear(){for(int i=1;i<=tot;i++)tr[i].num=tr[i].sum=tr[i].lson=tr[i].rson=0;tot=0;}
}T;
#define lson (rt<<1)
#define rson (rt<<1|1)
void insert(int rt,int l,int r,int al,int ar,int aw){
    int mid=l+r>>1;if(al<=mid&&mid<ar){vec[rt].pb(aw);return ;}
    if(l==r){ans[aw]=a[l];return ;}
    if(ar<=mid)insert(lson,l,mid,al,ar,aw);
    if(al>mid)insert(rson,mid+1,r,al,ar,aw);
}
priority_queue<int>qq[2];
priority_queue<int,vector<int>,greater<int> >pq[2];
void sakura(int rt,int l,int r){
    int siz=vec[rt].size(),mid=l+r>>1;
    if(l^r)sakura(lson,l,mid),sakura(rson,mid+1,r);if(!siz)return ;
    while(!qq[0].empty())qq[0].pop();
    while(!qq[1].empty())qq[1].pop();
    for(int i=mid;i>=l;i--){
        if(i+2>mid)root[i]=0;else root[i]=root[i+2];
        qq[i&1].push(p[i]);if(i+1<=mid)qq[i&1].push(p[i+1]);
        T.insert(root[i],root[i],1,n,qq[i&1].top(),1),qq[i&1].pop();
    }
    while(!pq[0].empty())pq[0].pop();
    while(!pq[1].empty())pq[1].pop();
    for(int i=mid+1;i<=r;i++){
        if(i-2<=mid)root[i]=0;else root[i]=root[i-2];
        if(i==mid+1)T.insert(root[i],root[i],1,n,p[i],1);
        else{
            int x=p[i],y=p[i-1];if(x<y)swap(x,y);pq[i&1].push(x);
            summ[i]=summ[i-2]+a[ord[x]];int t=pq[i&1].top();
            if(t<y)pq[i&1].pop(),pq[i&1].push(y),summ[i]+=a[ord[y]]-a[ord[t]],y=t;
            T.insert(root[i],root[i],1,n,y,1);
        }
    }
    for(int i=0;i<siz;i++){
        int x=vec[rt][i],L=s[x].l,R=s[x].r;LL res=0;
        if(R-L+1&1)res+=a[R],R--;
        if(R>mid){
            int tmp=(mid-L)/2+1,t=T.ask(root[L],root[R],1,n,tmp);
            res+=T.askSum(root[L],1,n,t,n)+T.askSum(root[R],1,n,t,n)+summ[R];
        }
        else res+=T.askSum(root[L],1,n,1,n);
        ans[x]=res;
    }
    for(int i=l;i<=r;i++)root[i]=summ[i]=0;T.clear();
}
bool cmp(int x,int y){return a[x]<a[y];}
int main(){
    read(n);read(q);
    for(int i=1;i<=n;i++)read(a[i]),ord[i]=i;
    for(int i=1;i<=q;i++)read(s[i].l),read(s[i].r),
        insert(1,1,n,s[i].l,s[i].r,i);
    sort(ord+1,ord+n+1,cmp);
    for(int i=1;i<=n;i++)p[ord[i]]=i;sakura(1,1,n);
    for(int i=1;i<=q;i++)printf("%lld\n",ans[i]);
    return 0;
}

谢谢!!!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值