码迷,mamicode.com
首页 > 其他好文 > 详细

BZOJ4518:[SDOI2016]征途——题解

时间:2018-05-12 22:28:56      阅读:150      评论:0      收藏:0      [点我收藏+]

标签:api   次数   cstring   推导   tps   blog   ace   ring   基础   

https://www.lydsy.com/JudgeOnline/problem.php?id=4518

https://www.luogu.org/problemnew/show/P4072

Pine开始了从S地到T地的征途。
从S地到T地的路可以划分成n段,相邻两段路的分界点设有休息站。
Pine计划用m天到达T地。除第m天外,每一天晚上Pine都必须在休息站过夜。所以,一段路必须在同一天中走完。
Pine希望每一天走的路长度尽可能相近,所以他希望每一天走的路的长度的方差尽可能小。
帮助Pine求出最小方差是多少。
设方差是v,可以证明,v×m^2是一个整数。为了避免精度误差,输出结果时输出v×m^2。

设第i段划分路的长度为ai,则不难推导出答案为m*sigma(a^2)-(sigma(a))^2。

维护前缀和s[i],于是设f[i]为前i条路的最少费用,就有f[i]=min(f[i],f[j]+(s[[i]-s[j])^2)。

愉快的斜率优化维护,大致操作跟BZOJ3675 & 洛谷3648 & UOJ104:[Apio2014]序列分割一样(是的这两道题几乎相同,可能式子不一样……?)复杂度O(nm)。

……然而这道题是在去apio之后看的wuvin课件写的,于是……这其实是裸的wqs二分。

二分划分代价为c,c越大划分次数越少,反之越多(因为c减小时,显然答案在原基础上变得更小,可以选择不继续划分(当然答案还是比原来小),或者可以多花点c来划分序列,于是划分一定变多。)将划分次数逼近为m则此时即为答案。

复杂度O(nlogV)快到不知道哪里去了,这题n可以为1e5

#include<queue>
#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef long double dl;
const int N=50005;
inline int read(){
    int X=0,w=0;char ch=0;
    while(!isdigit(ch)){w|=ch==-;ch=getchar();}
    while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
int n,m,q[N],g[N];
ll s[N],f[N];
inline ll sqr(ll x){return x*x;}
inline dl suan(int k,int j){
    if(s[k]==s[j])return -1e18;
    return (dl)(f[k]-f[j]+sqr(s[k])-sqr(s[j]))/(s[k]-s[j]);
}
bool pan(ll c){
    int l=0,r=0;
    for(int i=1;i<=n;i++){
        while(l<r&&suan(q[l],q[l+1])<(dl)2*s[i])l++;
        f[i]=f[q[l]]+sqr(s[i]-s[q[l]])+c;
        g[i]=g[q[l]]+1;
        while(l<r&&suan(q[r-1],q[r])>suan(q[r],i))r--;
        q[++r]=i;
    }
    return g[n]<=m;
}
ll solve(ll l,ll r){
    ll ans;
    while(l<r){
        ll mid=(l+r)>>1;
        if(!pan(mid))l=mid+1;
        else{
            ans=m*(f[n]-mid*m)-sqr(s[n]);r=mid;//这里括号里面的m改为g[n]就会wa不知道为什么有谁能解答一下吗
//主要是感觉最后反正g[n]也会变成m就乘了g[n]结果就90分了…… } }
return ans; } int main(){ n=read(),m=read(); for(int i=1;i<=n;i++)s[i]=s[i-1]+read(); printf("%lld\n",solve(0,sqr(s[n]))); return 0; }

+++++++++++++++++++++++++++++++++++++++++++

+本文作者:luyouqi233。               +

+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/ +

+++++++++++++++++++++++++++++++++++++++++++

BZOJ4518:[SDOI2016]征途——题解

标签:api   次数   cstring   推导   tps   blog   ace   ring   基础   

原文地址:https://www.cnblogs.com/luyouqi233/p/9030055.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!