1. 程式人生 > >bzoj5311: 貞魚

bzoj5311: 貞魚

還是年輕啊算的時候少乘一個4000被卡二分上界了。。。%%%%bright教我超級快速讀D飛bzoj垃圾卡常資料

我們容易寫出這樣的DP方程:f[i][j]=f[k][j-1]+val(k+1,j)

然後可以發現g(j)是單調減而且是下凸的

那麼我們就可以愉快的上wqs二分了

那麼f[i]就表示無限分最優解,就有f[i]=f[j]+val(j+1,i)+C

而這個明顯是四邊形不等式優化的形式

O(nlognlogw)踩了

#include<cstdio>
#include<iostream>
#include<cstring>
#include
<cstdlib> #include<algorithm> #include<cmath> using namespace std; typedef long long LL; const int MAXSIZE=1<<15; char buf[MAXSIZE],*p1=buf,*p2=buf; #define gc p1==p2&&(p2=(p1=buf)+fread(buf,1,MAXSIZE,stdin),p1==p2)?EOF:*p1++ int read() { int x=0,f=1;char
ch=gc; while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc;} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=gc;} return x*f; } int n,s[4100][4100]; int val(int j,int i){return (s[i][i]-s[i][j]-s[j][i]+s[j][j])/2;} struct node { int l,r,id; node(){} node(int L,int R,int
ID){l=L;r=R;id=ID;} }q[4100];int f[4100],g[4100]; void check(int C) { int h=1,t=0;q[++t]=node(1,n,0); f[0]=0;g[0]=0; for(int i=1;i<=n;i++) { if(q[h].r<i)h++; q[h].l=i; f[i]=f[q[h].id]+val(q[h].id,i)+C; g[i]=g[q[h].id]+1; if(h>t||f[i]+val(i,n)<=f[q[t].id]+val(q[t].id,n)) { while(h<=t&&f[i]+val(i,q[t].l)<=f[q[t].id]+val(q[t].id,q[t].l))t--; if(h>t)q[++t]=node(1,n,i); else { int l=q[t].l,r=q[t].r,ans; while(l<=r) { int mid=(l+r)/2; if(f[i]+val(i,mid)>f[q[t].id]+val(q[t].id,mid)) { ans=mid; l=mid+1; } else r=mid-1; } q[t].r=ans; q[++t]=node(ans+1,n,i); } } } } int main() { freopen("a.in","r",stdin); freopen("a.out","w",stdout); int K; n=read(),K=read(); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { s[i][j]=read(); s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1]; } int l=0,r=(1<<30)-1,ans; while(l<=r) { int mid=(l+r)/2; check(mid); if(g[n]>=K) { ans=f[n]-K*mid; l=mid+1; } else r=mid-1; } printf("%d\n",ans); return 0; }