2019雅礼集训 D7T2 subsequence [DP,平衡树]

题目描述:

样例:

input1:
5
-2 -8 0 5 -3

output1:
5 10 13 2 -13

input2:
6
-10 20 -30 40 -50 60

output2:
60 160 280 390 400 210

数据范围与约定:



考虑DP:令\(dp(i,j)\)表示前\(i\)个点选\(j\)个,能得到的最大价值。

得到转移方程:\(dp(i,j)=\max\{dp(i-1,j),dp(i-1,j-1)+a_i\cdot j\}\)

这个方程很明显是\(n^2\)的。

经过打表/分析样例/严谨证明(不可能的),可以发现结论:\(i\)相同时,转移方程中满足后一种决策

更优的\(j\)一定是一段后缀 。

(人话:只要求\(dp(i,j_0)\)时选了\(a_i\),那么对于所有\(j\geq j_0\),都会选择\(a_i\))

证明嘛,我看不懂,就直接复制上来啦

考虑选择后一个决策的条件:\(dp(i-1,j-1)+a_i\cdot j \geq dp(i-1,j)\),即\(a_i\cdot j\geq dp(i-1,j)-dp(i-1,j-1)\)。

于是我们可以用一棵平衡树来维护\(dp(i-1,j)-dp(i-1,j-1)?\)(即它的差分),在上面二分出\(j_0?\)后就可以插入这个位置并对后缀进行区间加了(本来是区间加上一个等差序列,但这里已经差分过了,就可以普通区间加了)。

复杂度\(O(n\log n)\),代码很短。

#include<bits/stdc++.h>
namespace my_std{
    using namespace std;
    #define mod 998244353
    #define pii pair<int,int>
    #define fir first
    #define sec second
    #define MP make_pair
    #define rep(i,x,y) for (int i=(x);i<=(y);i++)
    #define drep(i,x,y) for (int i=(x);i>=(y);i--)
    #define go(x) for (int i=head[x];i;i=edge[i].nxt)
    #define sz 200010
    typedef long long ll;
    template<typename T>
    inline void read(T& t)
    {
        t=0;char f=0,ch=getchar();
        double d=0.1;
        while(ch>‘9‘||ch<‘0‘) f|=(ch==‘-‘),ch=getchar();
        while(ch<=‘9‘&&ch>=‘0‘) t=t*10+ch-48,ch=getchar();
        if(ch==‘.‘)
        {
            ch=getchar();
            while(ch<=‘9‘&&ch>=‘0‘) t+=d*(ch^48),d*=0.1,ch=getchar();
        }
        t=(f?-t:t);
    }
    template<typename T,typename... Args>
    inline void read(T& t,Args&... args){read(t); read(args...);}
    void file(){freopen("a.txt","r",stdin);}
    inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;}
}
using namespace my_std;

namespace Splay
{
    ll w[sz],tag[sz];
    int fa[sz],ch[sz][2],size[sz],root,cnt;
    #define ls ch[x][0]
    #define rs ch[x][1]

    void Add(int x,ll t){w[x]+=t;tag[x]+=t;}
    void pushdown(int x){ ll &t=tag[x]; if (t) { Add(ls,t); Add(rs,t); } t=0; }
    void pushup(int x){size[x]=size[ls]+size[rs]+1;}
    bool get(int x){return ch[fa[x]][1]==x;}

    void rotate(int x)
    {
        int y=fa[x],z=fa[y],k=get(x),w=ch[x][!k];
        if (z) pushdown(z);pushdown(y);pushdown(x);
        if (z) ch[z][get(y)]=x;ch[x][!k]=y;ch[y][k]=w;
        if (w) fa[w]=y;fa[y]=x;fa[x]=z;
        pushup(y);pushup(x);
    }
    void splay(int x)
    {
        while (fa[x])
        {
            int y=fa[x];
            if (fa[y]) rotate(get(x)==get(y)?y:x);
            rotate(x);
        }
        root=x;
    }
    void insert(int &x,int f,ll cur,ll v)
    {
        if (!x)
        {
            x=++cnt;
            fa[x]=f;size[x]=1;w[x]=(cur+1)*v;
            return splay(x);
        }
        pushdown(x);
        int siz=cur+1+size[ls];
        if (w[x]>=v*siz) insert(rs,x,siz,v);
        else insert(ls,x,cur,v);
    }
    void getans(int x,ll &cur)
    {
        pushdown(x);
        if (ls) getans(ls,cur);
        printf("%lld ",cur+=w[x]);
        if (rs) getans(rs,cur);
    }
    #undef ls
    #undef rs
}
using namespace Splay;

int n;
ll a[sz];

int main()
{
    file();
    read(n);
    rep(i,1,n) read(a[i]);
    rep(i,1,n)
    {
        insert(root,0,0,a[i]);
        if (ch[root][1]) Add(ch[root][1],a[i]);
    }
    ll ans=0;
    getans(root,ans);
}

原文地址:https://www.cnblogs.com/p-b-p-b/p/10261047.html

时间: 2024-10-08 13:29:55

2019雅礼集训 D7T2 subsequence [DP,平衡树]的相关文章

2019雅礼集训 D7T1 inverse [概率/期望,DP]

题目描述: 样例: input1: 3 1 1 2 3 output1: 833333340 input2: 5 10 2 4 1 3 5 output2: 62258360 数据范围与约定: 概率/期望的常用套路:将许许多多个元素单独考虑,以达到解决问题的目的. 这里发现不可能整个序列一起考虑,于是枚举任意两个位置,计算出k次翻转之后左边大于右边的概率,再加起来就好了. 于是我们有了一个非常暴力的DP: 令\(dp(i,j,k)?\) 表示k次翻转之后i位置大于j位置的概率.为了方便我们强行令

2019雅礼集训 D10T1 数字重排 [DP]

题目描述: 样例: input: 5 5 5 10 17 23 output: 3 数据范围与约定: 简单DP,不做解释,直接搬题解. 标程: #include<bits/stdc++.h> using namespace std; const int N=1e5+5; int n,a[N],m,s; bitset<N>f,g; int main(){ freopen("sort.in","r",stdin); freopen("s

2019雅礼集训 D4T1 w [费用流]

题目描述: 样例: input1: 4 1 2 1 2 3 4 1 2 1 3 3 4 1 2 2 3 1 4 2 1 3 4 1 1 2 3 output1: 9 input2: 5 1 1 3 99 99 100 2 1 2 1 3 3 4 3 5 1 3 1 2 2 4 2 5 2 1 2 3 1 2 1 2 2 1 output2: 198 数据范围: 先放个原题地址:CF1061E. 毒瘤出题人搬原题差评 毒瘤出题人题目翻译出锅差评 这题看到如此不伦不类的问法,似乎不是dp.贪心等算法

【2019雅礼集训】【第一类斯特林数】【NTT&amp;多项式】permutation

目录 题意 输入格式 输出格式 思路: 代码 题意 找有多少个长度为n的排列,使得从左往右数,有a个元素比之前的所有数字都大,从右往左数,有b个元素比之后的所有数字都大. n<=2*10^5,a,b<=n 输入格式 输入三个整数n,a,b. 输出格式 输出一个整数,表示答案. 思路: 这道题是真的神啊... 首先,根据官方题解的思路,首先有一个n^2的DP: 定义dp[i][j]表示一个长度为i的排列,从前往后数一共有j个数字大于所有排在它前面的数字. 首先有转移式: \[dp[i][j]=d

2019雅礼集训 D10T2 硬币翻转 [交互题]

题目描述: coin.h: #include<string> void guess(); int ask(std::string coin); grader.cpp: #include "coin.h" #include <iostream> #include <assert.h> using namespace std; namespace U { using u64 = unsigned long long; using i64 = long l

#6030. 【雅礼集训 2017 Day1】矩阵

#6030. 「雅礼集训 2017 Day1」矩阵 题目描述 有一个 n×n  的矩阵,每个位置 (i,j) 如果是 . 表示为白色,如果是 # 表示为黑色. 初始时,每个位置可以是黑色或白色的,(i,j)  位置的值会作为 ai,j 给你. 现在有一种操作,选择两个整数 i,j∈[1,n],记 (i,1),(i,2),…,(i,n) (i, 1), (i, 2)的颜色为 C1,C2,…Cn ??,将 (1,j),(2,j),…,(n,j)  的颜色赋为 C1,C2,…,Cn ??. 你的任务是

雅礼集训——day1、day2

day1: 嗯上午考试拿了100分.第一题40,第二题60.看完题的时候我就觉得第二题的部分分是最好得到的,因为数据范围只有300,而且一眼看上去就是网络流的二分图多重匹配模型?然后就建了个网络流写了些,期望得分是70分,但是第1组数据有点劲,被卡掉了,就拿了60分.正解是map+set的贪心...并不会STL 写完T2去看T1,先用DFS乱搞了一下,结果样例都没过去,我手推了一下样例,得到了一个公式,就是从一个点出发需要加上的边数=这个点通过DFS能够遍历到的点的个数-与这个点直接相连的点的个

雅礼集训——day3、day4

day3: 上午考试就拿了10分... T1写了个N^3的暴力,然后就拿了10分...正解是要二分时间然后找到前m个脚,然后用二分套二分求第k大.... T2看了半天感觉并不可做...然后正解居然要用斐波拉契数列的通项来求解.... T3求最小生成树的个数,我以为这道题还是可以拿50分的,然后就没有然后了.推了2小时的样例然后强行把自己劝退了...正解是要用字母树+贪心... 要学的东西又多了好多,压力好大... 下午讲的字符串算法,最后的那个回文树不是很明白...这两天得学一下字符串算法 da

「6月雅礼集训 2017 Day10」quote

[题目大意] 一个合法的引号序列是空串:如果引号序列合法,那么在两边加上同一个引号也合法:或是把两个合法的引号序列拼起来也是合法的. 求长度为$n$,字符集大小为$k$的合法引号序列的个数.多组数据. $1 \leq T \leq 10^5, 1 \leq n \leq 10^7, 1\leq K \leq 10^9$ [题解] 显然引号序列可以看做括号序列,于是我们有了一个$O(n^2)$的dp了. 设$f_{i,j}$表示到第$i$个位置,前面有$j$个左引号没有匹配,的方案数 每次,要么有