[BZOJ3675] [Apio2014]序列分割

dp[i]=max{dp[j]+sum[j](sum[i]sum[j])}+C
dp[i]=max{dp[j]+sum[j]sum[i]sum[j]2}+C
j<kk
dp[j]+sum[j]sum[i]sum[j]2<=dp[k]+sum[k]sum[i]sum[k]2
(dp[j]sum[j]2)(dp[k]sum[k]2)<=sum[i](sum[k]sum[j])
(dp[j]sum[j]2)(dp[k]sum[k]2)sum[k]sum[j]<=sum[i]
y[j]y[k]<=sum[i](sum[k]sum[j])
y[j]y[k]sum[k]sum[j]<=sum[i]

dp[i]=min{dp[j]+(sum[i]sum[j])2}+C
dp[i]=min{dp[j]2sum[i]sum[j]+sum[j]2}+sum[i]2+C
j<kk
dp[j]2sum[i]sum[j]+sum[j]2>=dp[k]2sum[i]sum[k]+sum[k]2
(dp[j]+sum[j]2)(dp[k]+sum[k]2)>=2sum[i](sum[j]sum[k])
(dp[j]+sum[j]2)(dp[k]+sum[k]2)2(sum[j]sum[k])<=sum[i]
ans=sum[n]2dp[n]

传送门

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

题解

作大死~
要来数据本地AC,BZOJ上死活不过
神做法
对于 dp[i]=min{dp[j]+(sum[i]sum[j])2} 我们没办法控制切割段数
所以我们给它加个C
得到 dp[i]=min{dp[j]+(sum[i]sum[j])2}+C
显然,当C=+∞时,只分为了1段,当C=0时,分为了n段
所以我们二分C,每次判断段数即可

const
 maxn=100005;
var
 dp,y,s:array[0..maxn]of int64;
 t,w,v,ans:array[0..maxn]of longint;
 i,j,k,n,m,l,r:longint;
 ll,rr,mid:int64;
function check1(a,b,c:longint):boolean;
begin
 exit(dp[a]+(s[c]-s[a])*(s[c]-s[a])>=dp[b]+(s[c]-s[b])*(s[c]-s[b]));
end;

function check2(a,b,c:longint):boolean;
var d,e:real;
begin
 if (s[b]<>s[a])and(s[c]<>s[b])
 then begin
  d:=(y[c]-y[b])/(s[c]-s[b]);
  e:=(y[b]-y[a])/(s[b]-s[a]);
  exit(d<=e);  //注意这部分如果都按照else的写法会爆Pascal的int64
 end
 else exit((y[c]-y[b])*(s[b]-s[a])<=(y[b]-y[a])*(s[c]-s[b]));
end;

procedure check(c:int64);
begin
 l:=1;r:=1;t[1]:=0;y[0]:=0;dp[0]:=0;
 for i:=1 to n do
  begin
   while (l<r)and(check1(t[l],t[l+1],i)) do inc(l);
   dp[i]:=dp[t[l]]+(s[i]-s[t[l]])*(s[i]-s[t[l]])+c; w[i]:=w[t[l]]+1; y[i]:=dp[i]+s[i]*s[i]; v[i]:=t[l];
   while (l<r)and(check2(t[r-1],t[r],i)) do dec(r);
   inc(r); t[r]:=i;
  end;
end;

begin
 readln(n,m); inc(m); s[0]:=0;
 for i:=1 to n do
  begin read(s[i]); s[i]:=s[i]+s[i-1]; end;
 ll:=0;
 rr:=maxlongint*300000;
 while ll<rr do
  begin
   mid:=(ll+rr+1)>>1;
   check(mid);
   if w[n]>=m
   then ll:=mid
   else rr:=mid-1;
  end;
 check(ll);
 writeln((s[n]*s[n]-dp[n]+ll*m)>>1);
end.

你可能感兴趣的:([BZOJ3675] [Apio2014]序列分割)