21.6.1 t3

tag:平衡树,二分


最优策略中一个点一定只选一次,否则只需要选最后一次就行了。

在确定取的点集后,按 \(t\) 从小到大取最优。


考虑 \(n^2\) 暴力,\(s\) 最大的那个一定出现在所有方案中,否则用它替换第一个点一定更优。(这里只是说明最大的一定出现,不代表它一定是第一个

设最大的 \(s\)\(s_{mx}\),对于

  • \(t_i<t_{mx}\)\(s_i\) += \(t_{mx}\),表示选了 \(i\) 以后,\(mx\) 这一堆会往后拖一天

  • \(t_i\ge t_{mx}\)\(s_i\) += \(t_i\),表示 \(i\) 会往后拖一天

这样处理完以后就递归到了 \(n-1\) 的问题。


根据递归解法,发现选 \(k\) 个点的最优集合一定是某个选 \(k+1\) 个点的最优集合的子集。

所以最优方案选的点可以用一个排列 \(p_1\cdots p_n\) 表示,其中 \(p_1\cdots p_k\) 表示选 \(k\) 个点的最优集合。

考虑如何求出这个排列。

按照 \(t\) 从小到大插入,由于当前插入的 \(t\) 是当前最大的,所以这个点在上文的暴力(只考虑当前插入的点)中,递归到第 \(i\) 层问题时的 \(s'=t\cdot i+s\),所以问题变为这个 \(s'\) 在递归到哪一层问题时会成为最大值,并被选入集合。

这个问题是具有二分性的,因为当前插入的 \(t\) 是最大的,所以它的 \(s'\) 的增长速度是最快的,在第一次成为最大值之后的每一层递归中都是最大值。

而且因为当前 \(t\) 是最大的,所以对于当前答案排列中它后面的所有 \(s_i\) 都要加上 \(t\)(根据上文暴力)。

因为涉及到动态插入,二分和区间加,可以用平衡树解决。

最终平衡树的中序遍历的前 \(k\) 项求和就是选 \(k\) 个的答案。

复杂度 \(O(nlogn)\)


代码实现上,插入可以直接跟在二分后面,不用找rk再找位置。

#include<bits/stdc++.h>
using namespace std;

template<typename T>
inline void Read(T &n){
	char ch; bool flag=false;
	while(!isdigit(ch=getchar()))if(ch=='-')flag=true;
	for(n=ch^48;isdigit(ch=getchar());n=(n<<1)+(n<<3)+(ch^48));
	if(flag)n=-n;
}

enum{
    MAXN = 500005
};

typedef long long ll;

int n;

struct node{
    int son[2], fa, sz; ll val, add;
    #define lc(x) t[x].son[0]
    #define rc(x) t[x].son[1]
    #define fa(x) t[x].fa
    #define sz(x) t[x].sz
    #define val(x) t[x].val
    #define add(x) t[x].add
}t[MAXN];
int node_cnt, root;

inline char Which(int x){return rc(fa(x))==x;}
inline void Push_Up(int x){sz(x) = sz(lc(x))+sz(rc(x))+1;}

inline void Add(int x, ll add){
    add(x) += add;
    val(x) += add;
}

inline void Push_Down(int x){
    if(add(x)){
        if(lc(x)) Add(lc(x),add(x));
        if(rc(x)) Add(rc(x),add(x));
        add(x) = 0;
    }
}

inline void Rotate(int x){
    int y=fa(x), z=fa(y), k=Which(x), u=t[x].son[!k];
    if(z) t[z].son[Which(y)]=x; else root=x; fa(x)=z;
    if(u) fa(u)=y; t[y].son[k]=u;
    fa(y)=x; t[x].son[!k]=y;
    Push_Up(y); Push_Up(x);
}

inline void Splay(int x, int Tar=0){
    while(fa(x)!=Tar){
        int y=fa(x), z=fa(y);
        if(z!=Tar) Rotate(Which(x)==Which(y)?y:x);
        Rotate(x);
    }
}

struct ele{
    ll s, t;
    inline ll f(int x){return s+t*(x-1);}
    inline bool operator <(const ele &k)const{return t<k.t;}
}a[MAXN];

void Insert(int &x, int y, ele k, int rk){
    if(!x){
        x = ++node_cnt;
        val(x) = k.f(rk+1);
        fa(x) = y; sz(x) = 1;
        int tx=x;
        Splay(tx);
        if(rc(tx)) Add(rc(tx),k.t);
        return;
    }
    Push_Down(x);
    if(k.f(rk+sz(lc(x))+1) < val(x)) Insert(rc(x),x,k,rk+sz(lc(x))+1);
    else Insert(lc(x),x,k,rk);
}

ll s;
void Check(int x){
    if(!x) return;
    Push_Down(x);
    Check(lc(x));
    s += val(x); cout<<s<<'\n';
    Check(rc(x));
}

int main(){
    // freopen("3.in","r",stdin);
    // freopen("3.out","w",stdout);
    Read(n);
    for(register int i=1; i<=n; i++) Read(a[i].s), Read(a[i].t);
    sort(a+1,a+n+1);
    for(register int i=1; i<=n; i++) Insert(root,0,a[i],0);// Check(root), puts("");
    Check(root);
    return 0;
}
posted @ 2021-06-25 09:04  oisdoaiu  阅读(25)  评论(0编辑  收藏  举报