• 题解 [JSOI2011]柠檬


    题目传送门

    题目大意

    给出一个区间,每个点都有一个颜色,把这个区间分为许多块,每一块的权值为 (max{s imes t^2}) ,其中 (s) 为某种颜色,(t) 为该颜色在该块中出现的次数。问最大权值之和。

    (nle 10^5,s_ile 10^4)

    思路

    话说用笔记本打代码真的好难受啊!!!

    首先我们可以看出这个肯定是个 dp ,可以列出 dp 式:

    [dp[i]=max{dp[j-1]+ ext{count}(j o i)} ]

    于是问题就是如何求出 ( ext{count}(j o i))

    我们经过思(mang)考(cai)发现,其实在最优情况下,每一段的两端的颜色一定是相同的,而且产生贡献的颜色也一定是两端的颜色。

    至于证明的话可以感性理解一下就是说,如果不同的话可以把一段分到另外一边,这样不会让答案变劣。

    然后,我们发现这下就可以做了。我们可以设 (p_{i,j}) 表示颜色 (i)(j) 个出现的数的位置,那我们就可以得到转移式:

    [f[p_{i,j}]=max{f[p_{i,a}-1]+i(j-a+1)^2|ale j} ]

    然后你发现这个东西就可以斜率优化了,最后的式子就是:

    [frac{(f[p_{i,b}-1]+b^2i)-(f[p_{i,a}-1]+a^2i)}{b-a}>2i(j+1) ]

    的时候,(b)(a) 更优。

    然后你发现这个东西就是一个上凸壳,然后你每次弹得时候就是把队尾弹出来(因为上凸壳斜率递减,对于相同的 (i),它的 (2i(j+1)) 单调递增)。

    于是时间复杂度就是 (Theta(n))

    ( exttt{Code})

    #include <bits/stdc++.h>
    using namespace std;
    
    #define Int register int
    #define int long long
    #define MAXN 100005
    
    template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
    template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
    template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
    
    vector <int> stk[MAXN];
    int top1 (vector <int> &vec){return vec[vec.size() - 1];}
    int top2 (vector <int> &vec){return vec[vec.size() - 2];}
    int n,s[MAXN],c[MAXN],f[MAXN],tot[MAXN];
    
    int Y (int i){return f[i - 1] + c[i] * c[i] * s[i];}
    int X (int i){return c[i];}
    int calc (int x,int y){return f[y - 1] + s[x] * (c[x] - c[y] + 1) * (c[x] - c[y] + 1);}
    double Slope (int x,int y){return (Y(y) - Y(x)) * 1.0 / (X(y) - X(x));}
    
    signed main(){
    	read (n);
    	for (Int i = 1;i <= n;++ i) read (s[i]),c[i] = ++ tot[s[i]];
    	for (Int i = 1;i <= n;++ i){
    		int col = s[i],ind = c[i] + 1;
    		while (stk[col].size() >= 2 && Slope (top2(stk[col]),top1(stk[col])) <= Slope (top1(stk[col]),i)) stk[col].pop_back ();
    		stk[col].push_back (i);
    		while (stk[col].size() >= 2 && Slope(top2(stk[col]),top1(stk[col])) <= 2 * s[i] * (c[i] + 1)) stk[col].pop_back ();
    		f[i] = calc (i,top1(stk[col]));
    	}
    	write (f[n]),putchar ('
    ');
    	return 0;
    }
    
  • 相关阅读:
    常见图片格式PNG,JPEG,BMP,GIF区别总结
    sql在所有存储过程中查询包含某字符串的执行语句
    数字取整或保留小数四舍五入的正确写法
    SVG路径path的贝塞尔曲线指令
    查询总耗CPU最多与平均耗CPU最多的SQL语句
    MIME 参考手册
    SQL语句复制父子级表数据
    去掉数字格式结尾多余的零,补充数字格式结尾需要的零
    设置微信分享的标题 缩略图 连接 描述
    linux环境下php开启redis扩展(centos6.8)
  • 原文地址:https://www.cnblogs.com/Dark-Romance/p/13627324.html
Copyright © 2020-2023  润新知