1. 程式人生 > >BZOJ4654 NOI2016國王飲水記(動態規劃+三分)

BZOJ4654 NOI2016國王飲水記(動態規劃+三分)

  有很多比較顯然的性質。首先每個城市(除1外)至多被連通一次,否則沒有意義。其次將城市按水位從大到小排序後,用以連通的城市集合是一段字首,並且不應存在比1城市還小的。然後如果確定了選取的城市集合,每次選擇也應該是連續的一段,且應從小到大選,這樣保證了將其他城市的水儘量分到1,而不是被另外的城市分流。同時也說明如果不考慮次數限制應該劃分的儘量多。

  考慮怎麼用這些性質做。按水位從小到大排序,考慮大力dp,即設f[i][j]為前i個城市(可以不全選)分了j組時的答案,轉移即f[i][j]=max{(f[k][j-1]+si-sk)/(i-k+1)}。轉移顯然可以看做是找該點與其他點的斜率最大值,可以在下凸殼上三分。於是可以得到一個O(nkplogn)的優秀演算法。

  感覺上這個高精度的保留位數實在有點唬人,哪來那麼大誤差啊?於是考慮直接用long double,記錄轉移點,最後再用高精度計算答案。直接從複雜度裡去掉了一個p,效果拔群。獲得了82分的好成績,3T1WA。瞎猜了一發答案隨分組數量變化是個凸函式,搞了個wqs二分上去,非常慘的假掉了。然後突然發現之前的82分程式碼算斜率甚至忘了return,加上之後獲得了94分的好成績,get了兩個WA。再冷靜一下發現之前因為空間不夠把陣列開小了,改成滾動之後又過掉最後一個點,獲得了97分的好成績。然後想起來沒有把<h1的去掉因為感覺沒有必要,去掉之後,就過了。

  就這麼水過算了我不管了反正正解那種結論怎麼猜的到啊。

//以下程式碼刪除了高精度類
#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include <string>
#include<algorithm>
using namespace std;
#define ll long long
#define N 8010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'
a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,m,p,a[N],b[N],from[N][N],q[N],h; long double f[2][N]; Decimal calc(int m,int n) { if (m==0) return Decimal(h); return (calc(m-1,from[m][n])+a[n]-a[from[m][n]])/(n-from[m][n]+1); } long double slope(int j,int x,int y){return ((a[y]-f[j][y])-(a[x]-f[j][x]))/(y-x);} int main() { #ifndef ONLINE_JUDGE freopen("bzoj4654.in","r",stdin); freopen("bzoj4654.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read(),m=read(),p=read(); for (int i=1;i<=n;i++) a[i]=read();h=a[1]; int t=0;for (int i=1;i<=n;i++) if (a[i]>h) b[++t]=a[i]; sort(b+1,b+t+1);n=t;for (int i=1;i<=n;i++) a[i]=b[i]; for (int i=1;i<=n;i++) a[i]+=a[i-1]; for (int i=0;i<=n;i++) f[0][i]=h; for (int j=1;j<=min(n,m);j++) { int tail=0;memset(f[j&1],0,sizeof(f[j&1])); for (int i=1;i<=n;i++) { while (tail>1&&slope(j&1^1,q[tail-1],q[tail])>slope(j&1^1,q[tail],i-1)) tail--; q[++tail]=i-1; int l=1,r=tail; while (l+3<r) { int mid1=(l+r>>1)-1,mid2=(l+r>>1)+1; if ((f[j&1^1][q[mid1]]+a[i]-a[q[mid1]])*(i-q[mid2]+1)<(f[j&1^1][q[mid2]]+a[i]-a[q[mid2]])*(i-q[mid1]+1)) l=mid1;else r=mid2; } for (int k=l;k<=r;k++) if (f[j&1^1][q[k]]+a[i]-a[q[k]]>f[j&1][i]*(i-q[k]+1)) f[j&1][i]=(f[j&1^1][q[k]]+a[i]-a[q[k]])/(i-q[k]+1),from[j][i]=q[k]; } } cout<<calc(min(n,m),n).to_string(p+1); return 0; }