zoukankan      html  css  js  c++  java
  • BZOJ 3675 序列分割

    Description

    小H最近迷上了一个分割序列的游戏。在这个游戏里,小H需要将一个长度为N的非负整数序列分割成(k+1)个非空的子序列。为了得到(k+1)个子序列,小H将重复进行七次以下的步骤:
    (1.)小H首先选择一个长度超过1的序列(一开始小H只有一个长度为(n)的序列一一也就是一开始得到的整个序列);
    (2.)选择一个位置,并通过这个位置将这个序列分割成连续的两个非空的新序列。
    每次进行上述步骤之后,小H将会得到一定的分数。这个分数为两个新序列中元素和的乘积。小H希望选择一种最佳的分割方案,使得(k)轮(次)之后,小H的总得分最大。

    Input

    输入文件的第一行包含两个整数(n)(k)(k+1 le n))。
    第二行包含(n)个非负整数(a_{1},a_{2},cdots,a_{n}(0 le a_{i} le 10^{4})),表示一开始小H得到的序列。

    Output

    一行包含一个整数,为小H可以得到的最大得分。

    Sample Input

    7 3
    4 1 3 4 0 2 3

    Sample Output

    108

    HINT

    数据满足(2 le n le 100000,1 le k le min(n-1,200))

    我们做这道题首先要知道一个结论:对于一个划分最终态,它的答案一定是确定的,与划分的顺序没有任何的关系。
    证明直接从所求出发,假设第(i)块的和为(sum_{i}),那么$$ans = sum_{i=1}^{k+1} sum_{j=i+1}^{k+1} sum_{i}sum_{k}$$
    于是我们不难想到状态(f_{i,j})表示序列前(i)个分成(j)段的最大值。(pre_{i})表示(i)的前缀和,不难想到转移:$$f_{i,j}=f_{k,j-1}+pre_{k}(pre_{i}-pre_{k})$$
    然后就是什么单调栈啊,斜率优化上来优化这个dp即可。

    讲讲斜率优化怎么搞吧(会的可以无视这段话):
    将转移式子化简一下可得:$$f_{i,j}=pre_{i}pre_{k}+f_{k,j-1}-pre_{k}^{2}$$
    令$$x = pre_{k},y = f_{k,j-1}-pre_{k}^{2}$$
    则$$f_{i,j}=pre_{i}x+y$$
    即用一条斜率为(-pre_{i})直线截平面上一堆的点((x,y))得到最大截距。由于(x)单调,单调队列维护即可。

    #include<cstdio>
    #include<cstdlib>
    using namespace std;
    
    #define maxn (100010)
    #define maxk (210)
    typedef long long ll;
    int N,K,seq[maxn],head[2],tail[2]; ll pre[maxn],f[2][maxn];
    struct node { ll x,y; int ord; }team[2][maxn];
    
    inline bool Cmp(const node &a,const node &b,ll k)
    {
    	if (a.x == b.x) return true;
    	return a.y-b.y<k*(a.x-b.x);
    }
    inline void poph(int now,ll k,int id)
    {
    	while (tail[now]-head[now] >= 2&&team[now][head[now]+1].ord<id-1&&Cmp(team[now][head[now]+1],team[now][head[now]+2],k)) ++head[now];
    }
    inline ll front(int now,ll k) { return -k*team[now][head[now]+1].x+team[now][head[now]+1].y; }
    inline bool cmp(const node &a,const node &b,const node &c,const node &d)
    {
    	if (a.x == b.x) return false;
    	if (c.x == d.x) return true;
    	return (c.x-d.x)*(a.y-b.y)<(a.x-b.x)*(c.y-d.y);
    }
    inline void ins(int now,const node &p)
    {
    	while (tail[now]-head[now] >= 2&&cmp(team[now][tail[now]],team[now][tail[now]-1],p,team[now][tail[now]])) --tail[now];
    	team[now][++tail[now]] = p;
    }
    
    int main()
    {
    	freopen("3675.in","r",stdin);
    	freopen("3675.out","w",stdout);
    	scanf("%d %d",&N,&K);
    	for (int i = 1;i <= N;++i) scanf("%d ",seq+i),pre[i] += (ll)seq[i]+pre[i-1];
    	for (int j = 1;j <= K+1;++j)
    	{
    		int p = j&1,q = p^1;
    		head[q] = tail[q] = 0;
    		for (int i = j;i <= N;++i)
    		{
    			poph(p,-pre[i],i);
    			f[p][i] = front(p,-pre[i]);
    			ins(q,(node){pre[i],f[p][i]-pre[i]*pre[i],i});
    		}
    	}
    	printf("%lld
    ",f[(K+1)&1][N]);
    	fclose(stdin); fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    Hadoop常用命令
    常用MySQL语句整合
    如何把mysql的列修改成行显示数据简单实现
    如何在Linuxt系统下运行maven项目
    常用的SSH注解标签
    SSH整合框架+mysql简单的实现
    实现会话跟踪的技术有哪些
    Java 中访问数据库的步骤?Statement 和PreparedStatement 之间的区别?
    REST WebService与SOAP WebService的比较
    如何用java生成随机验证码
  • 原文地址:https://www.cnblogs.com/mmlz/p/4456484.html
Copyright © 2011-2022 走看看