zoukankan      html  css  js  c++  java
  • 【BZOJ】3675: [Apio2014]序列分割

    http://www.lydsy.com/JudgeOnline/problem.php?id=3675

    题意:给一个n个数字的序列,每一次分割的贡献是$sum(left, mid)*sum(mid+1, right)$,其中$left$表示本序列的最左边,$right$同理,$mid$是分割的位置(即在$mid$和$mid+1$中分割)。每次分割序列会变成两半。问分割k次得到的最大贡献和。n<=100000, k<=200

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=100005;
    ll s[N], d[2][N];
    int n, K, q[N], fr, ta;
    int main() {
    	scanf("%d%d", &n, &K);
    	for(int i=1; i<=n; ++i) scanf("%lld", &s[i]), s[i]+=s[i-1];
    	ll *now=d[0], *last=d[1];
    	for(int p=1; p<=K; ++p) {
    		fr=ta=0; q[ta++]=0;
    		for(int i=1; i<=n; ++i) {
    			while(fr!=ta-1 && last[q[fr]]-last[q[fr+1]]<=(s[q[fr]]-s[q[fr+1]])*(s[n]-s[i])) fr++;
    			int j=q[fr];
    			now[i]=last[j]+(s[i]-s[j])*(s[n]-s[i]);
    			while(fr!=ta-1 && (last[i]-last[q[ta-2]])*(s[q[ta-1]]-s[q[ta-2]])>=(last[q[ta-1]]-last[q[ta-2]])*(s[i]-s[q[ta-2]])) --ta;
    			q[ta++]=i;
    		}
    		swap(last, now);
    	}
    	ll ans=0;
    	for(int i=1; i<=n; ++i) ans=max(ans, last[i]);
    	printf("%lld
    ", ans);
    	return 0;
    }
    

      

    听laekov说要分析一下特殊的性质,于是分析了一下。。可以发现,每一个块对答案的贡献是$sum(本块)*sum(剩下的元素)$,最后当然要除以2,因为重复算了两次。但是可以用乱搞一下。。。

    考虑dp,设$d(i, j)$表示这个序列在$i$分割了$j$次得到的答案且第$j$次是在$i$分割的。

    容易得到:

    $d(i, j)=max(d(k, j-1)+sum(k+1, i)*sum(i+1, n))$

    大概就是表示得到的块为$(k+1, i)$,然后由于前面算过了对这些值的乘积,所以不用再计算一次(否则答案要除以二= =),于是我们直接乘一下后面的和即可。。

    然后另$s(n)=sum_{i=1}^{n} a[i]$,则

    $d(i, j)=max(d(k, j-1)+(s(i)-s(k))*(s(n)-s(i)))$

    然后搞搞斜率优化即可= =

    (好久没写了然后发现自己维护上凸壳时整个人sb了。。。。队尾居然不是维护凸壳然后交了4发wa居然没发现。。

  • 相关阅读:
    Vuex核心属性(上)
    layui 将json字符串以表格的形式展现出来
    Vue 路由懒加载 和 路由导航守卫
    jq遍历服务器发送过来的json字符串
    前端的发展
    关于定义函数的几种方式 及(箭头函数)
    js 子页面获取父页面的值
    关于数组的响应式方法和非响应式方法
    python3 -- random 模块
    多任务编程 -- multiprocessing 模块(创建多进程、进程池使用、进程间通信)
  • 原文地址:https://www.cnblogs.com/iwtwiioi/p/4488743.html
Copyright © 2011-2022 走看看