Luogu1721 [NOI2016]国王饮水记
https://www.luogu.com.cn/problem/P1721
斜率优化DP/高精度小数
高精度小数类已经免费提供啦(真是个好东西)
现在,我们钦定\(1\)号城市水位高为\(h_0\),把别的城市水位合并到\(h_0\)中
显然,那些水位比\(h_0\)低的城市根本无需考虑
所以我们每次肯定把\(h_0\)与水位较大的城市取合并
我们应该先与水位较低的合并,再与水位较高的合并
例:
\[h_1<h_2<h_3\\
\frac{\frac{h_1+h_2}{2}+h_3}{2}>\frac{\frac{h_1+h_3}{2}+h_2}{2}\\
\]
所以我们应该排个序,一段一段取,把所有的都取完一定是最优的(不取完的话,未取的\(h_i\)一定大于前面\(h_0 \cdots h_{i-1}\)合并得的最大值,还不如合并进去)
记录\(s_i=\sum_{j=1}^i h_j\)
设\(dp_{i,j}\)表示前\(i\)个数,取了\(j\)段的方案数
来一发朴素\(dp\)
\[dp_{i,j}=\max_{0 \le k < i} {\frac{dp_{k,j-1}+s_i-s_k}{k-i+1}}
\]
我们滚动一下,记\(predp_{i}=dp_{i,j-1}\),把\(dp\)数组转化为一维的
\[dp_{i}=\max_{0 \le j < i} {\frac{predp_{j}+s_i-s_j}{j-i+1}}
\]
\(j\)比\(k\)更优\((j<k)\),当且仅当:
\[\frac{predp_{j}+s_i-s_j}{i-j+1}>\frac{predp_{k}+s_i-s_k}{i-k+1}\\
\frac{predp_j-s_j}{i-j+1}-\frac{predp_k-s_k}{i-k+1}>\frac{s_i}{i-k+1}-\frac{s_i}{i-j+1}\\
\frac{(i-k+1)(predp_j-s_j)-(i-j+1)(predp_k-s_k)}{(i-j+1)(i-k+1)}>\frac{(i-j+1)s_i-(i-k+1)s_i}{(i-j+1)(i-k+1)}\\
(i-k+1)(predp_j-s_j)-(i-j+1)(predp_k-s_k)>(k-j)s_i\\
\frac{(i-k+1)(predp_j-s_j)-(i-j+1)(predp_k-s_k)}{k-j}>s_i
\]
\(s_i\)单调递增,同时维护斜率单调递增,斜率优化正常操作
时间复杂度\(O(n^2p)\)
嗯!居然\(T\)了!
需要一些奇葩操作,可以证明,我们最多会分\(14\)段不是长度为\(1\)的段
让我们尝试一下吧!
首先,对于较小的数,我们肯定尽量多的分为一段(意会即可)
~!@#¥%……&×
无耻进入 自闭退回
假装我们已经会了
那么我们直接跑\(\min n,k,14\)次\(dp\),最后一次性处理所有长度为\(1\)的段就好了!
先放主程序(以免被高精小数类淹没)
#define N 8005
int Fn,n,k,p,H,x,h[N],q[N],s[N];
Decimal predp[N],dp[N];
Decimal ans,g[N];
#define slope(i,j,k) (((i-k+1)*g[j].to_double()-(i-j+1)*g[k].to_double())/(k-j))
template<typename T>
void read(T &s)
{
s=0;
char ch=getchar();
while (!isdigit(ch))
ch=getchar();
while (isdigit(ch))
s=s*10+ch-'0',ch=getchar();
}
template<typename T>
void write(T s)
{
if (s>9)
write(s/10);
putchar(s%10+'0');
}
int main()
{
read(Fn),read(k),read(p);
read(H);
for (int i=2;i<=Fn;i++)
{
read(x);
if (x>H)
h[++n]=x;
}
k=min(k,n);
sort(h+1,h+n+1);
for (int i=1;i<=n;i++)
s[i]=s[i-1]+h[i];
for (int i=0;i<=n;i++)
dp[i]=H;
int lim=min(k,14);
int kz=n-k+lim;
for (int j=1;j<=lim;j++)
{
g[0]=dp[0];
predp[0]=dp[0];
for (int i=1;i<=kz;i++)
g[i]=dp[i]-s[i],predp[i]=dp[i];
int l=1,r=0;
q[++r]=0;
for (int i=1;i<=kz;i++)
{
while (l<r && slope(i,q[l],q[l+1])<=s[i])
l++;
int t=q[l];
dp[i]=(predp[t]+s[i]-s[t])/(i-t+1);
while (l<r && slope(i,q[r-1],q[r])>=slope(i,q[r],i))
r--;
q[++r]=i;
}
}
ans=dp[kz];
for (int i=kz+1;i<=n;i++)
ans=(ans+h[i])/2;
cout << ans.to_string(p+2) << endl;
return 0;
}
上完整代码吧!
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<string>
using namespace std;
// ---------- decimal lib start ----------
const int PREC = 3605;
class Decimal {
public:
Decimal();
Decimal(const std::string &s);
Decimal(const char *s);
Decimal(int x);
Decimal(long long x);
Decimal(double x);
bool is_zero() const;
// p (p > 0) is the number of digits after the decimal point
std::string to_string(int p) const;
double to_double() const;
friend Decimal operator + (const Decimal &a, const Decimal &b);
friend Decimal operator + (const Decimal &a, int x);
friend Decimal operator + (int x, const Decimal &a);
friend Decimal operator + (const Decimal &a, long long x);
friend Decimal operator + (long long x, const Decimal &a);
friend Decimal operator + (const Decimal &a, double x);
friend Decimal operator + (double x, const Decimal &a);
friend Decimal operator - (const Decimal &a, const Decimal &b);
friend Decimal operator - (const Decimal &a, int x);
friend Decimal operator - (int x, const Decimal &a);
friend Decimal operator - (const Decimal &a, long long x);
friend Decimal operator - (long long x, const Decimal &a);
friend Decimal operator - (const Decimal &a, double x);
friend Decimal operator - (double x, const Decimal &a);
friend Decimal operator * (const Decimal &a, int x);
friend Decimal operator * (int x, const Decimal &a);
friend Decimal operator / (const Decimal &a, int x);
friend bool operator < (const Decimal &a, const Decimal &b);
friend bool operator > (const Decimal &a, const Decimal &b);
friend bool operator <= (const Decimal &a, const Decimal &b);
friend bool operator >= (const Decimal &a, const Decimal &b);
friend bool operator == (const Decimal &a, const Decimal &b);
friend bool operator != (const Decimal &a, const Decimal &b);
Decimal & operator += (int x);
Decimal & operator += (long long x);
Decimal & operator += (double x);
Decimal & operator += (const Decimal &b);
Decimal & operator -= (int x);
Decimal & operator -= (long long x);
Decimal & operator -= (double x);
Decimal & operator -= (const Decimal &b);
Decimal & operator *= (int x);
Decimal & operator /= (int x);
friend Decimal operator - (const Decimal &a);
// These can't be called
friend Decimal operator * (const Decimal &a, double x);
friend Decimal operator * (double x, const Decimal &a);
friend Decimal operator / (const Decimal &a, double x);
Decimal & operator *= (double x);
Decimal & operator /= (double x);
private:
static const int len = PREC / 9 + 1;
static const int mo = 1000000000;
static void append_to_string(std::string &s, long long x);
bool is_neg;
long long integer;
int data[len];
void init_zero();
void init(const char *s);
};
Decimal::Decimal() {
this->init_zero();
}
Decimal::Decimal(const char *s) {
this->init(s);
}
Decimal::Decimal(const std::string &s) {
this->init(s.c_str());
}
Decimal::Decimal(int x) {
this->init_zero();
if (x < 0) {
is_neg = true;
x = -x;
}
integer = x;
}
Decimal::Decimal(long long x) {
this->init_zero();
if (x < 0) {
is_neg = true;
x = -x;
}
integer = x;
}
Decimal::Decimal(double x) {
this->init_zero();
if (x < 0) {
is_neg = true;
x = -x;
}
integer = (long long)x;
x -= integer;
for (int i = 0; i < len; i++) {
x *= mo;
if (x < 0) x = 0;
data[i] = (int)x;
x -= data[i];
}
}
void Decimal::init_zero() {
is_neg = false;
integer = 0;
memset(data, 0, len * sizeof(int));
}
bool Decimal::is_zero() const {
if (integer) return false;
for (int i = 0; i < len; i++) {
if (data[i]) return false;
}
return true;
}
void Decimal::init(const char *s) {
this->init_zero();
is_neg = false;
integer = 0;
// find the first digit or the negative sign
while (*s != 0) {
if (*s == '-') {
is_neg = true;
++s;
break;
} else if (*s >= 48 && *s <= 57) {
break;
}
++s;
}
// read the integer part
while (*s >= 48 && *s <= 57) {
integer = integer * 10 + *s - 48;
++s;
}
// read the decimal part
if (*s == '.') {
int pos = 0;
int x = mo / 10;
++s;
while (pos < len && *s >= 48 && *s <= 57) {
data[pos] += (*s - 48) * x;
++s;
x /= 10;
if (x == 0) {
++pos;
x = mo / 10;
}
}
}
}
void Decimal::append_to_string(std::string &s, long long x) {
if (x == 0) {
s.append(1, 48);
return;
}
char _[30];
int cnt = 0;
while (x) {
_[cnt++] = x % 10;
x /= 10;
}
while (cnt--) {
s.append(1, _[cnt] + 48);
}
}
std::string Decimal::to_string(int p) const {
std::string ret;
if (is_neg && !this->is_zero()) {
ret = "-";
}
append_to_string(ret, this->integer);
ret.append(1, '.');
for (int i = 0; i < len; i++) {
// append data[i] as "%09d"
int x = mo / 10;
int tmp = data[i];
while (x) {
ret.append(1, 48 + tmp / x);
tmp %= x;
x /= 10;
if (--p == 0) {
break;
}
}
if (p == 0) break;
}
if (p > 0) {
ret.append(p, '0');
}
return ret;
}
double Decimal::to_double() const {
double ret = integer;
double k = 1.0;
for (int i = 0; i < len; i++) {
k /= mo;
ret += k * data[i];
}
if (is_neg) {
ret = -ret;
}
return ret;
}
bool operator < (const Decimal &a, const Decimal &b) {
if (a.is_neg != b.is_neg) {
return a.is_neg && (!a.is_zero() || !b.is_zero());
} else if (!a.is_neg) {
// a, b >= 0
if (a.integer != b.integer) {
return a.integer < b.integer;
}
for (int i = 0; i < Decimal::len; i++) {
if (a.data[i] != b.data[i]) {
return a.data[i] < b.data[i];
}
}
return false;
} else {
// a, b <= 0
if (a.integer != b.integer) {
return a.integer > b.integer;
}
for (int i = 0; i < Decimal::len; i++) {
if (a.data[i] != b.data[i]) {
return a.data[i] > b.data[i];
}
}
return false;
}
}
bool operator > (const Decimal &a, const Decimal &b) {
if (a.is_neg != b.is_neg) {
return !a.is_neg && (!a.is_zero() || !b.is_zero());
} else if (!a.is_neg) {
// a, b >= 0
if (a.integer != b.integer) {
return a.integer > b.integer;
}
for (int i = 0; i < Decimal::len; i++) {
if (a.data[i] != b.data[i]) {
return a.data[i] > b.data[i];
}
}
return false;
} else {
// a, b <= 0
if (a.integer != b.integer) {
return a.integer < b.integer;
}
for (int i = 0; i < Decimal::len; i++) {
if (a.data[i] != b.data[i]) {
return a.data[i] < b.data[i];
}
}
return false;
}
}
bool operator <= (const Decimal &a, const Decimal &b) {
if (a.is_neg != b.is_neg) {
return a.is_neg || (a.is_zero() && b.is_zero());
} else if (!a.is_neg) {
// a, b >= 0
if (a.integer != b.integer) {
return a.integer < b.integer;
}
for (int i = 0; i < Decimal::len; i++) {
if (a.data[i] != b.data[i]) {
return a.data[i] < b.data[i];
}
}
return true;
} else {
// a, b <= 0
if (a.integer != b.integer) {
return a.integer > b.integer;
}
for (int i = 0; i < Decimal::len; i++) {
if (a.data[i] != b.data[i]) {
return a.data[i] > b.data[i];
}
}
return true;
}
}
bool operator >= (const Decimal &a, const Decimal &b) {
if (a.is_neg != b.is_neg) {
return !a.is_neg || (a.is_zero() && b.is_zero());
} else if (!a.is_neg) {
// a, b >= 0
if (a.integer != b.integer) {
return a.integer > b.integer;
}
for (int i = 0; i < Decimal::len; i++) {
if (a.data[i] != b.data[i]) {
return a.data[i] > b.data[i];
}
}
return true;
} else {
// a, b <= 0
if (a.integer != b.integer) {
return a.integer < b.integer;
}
for (int i = 0; i < Decimal::len; i++) {
if (a.data[i] != b.data[i]) {
return a.data[i] < b.data[i];
}
}
return true;
}
}
bool operator == (const Decimal &a, const Decimal &b) {
if (a.is_zero() && b.is_zero()) return true;
if (a.is_neg != b.is_neg) return false;
if (a.integer != b.integer) return false;
for (int i = 0; i < Decimal::len; i++) {
if (a.data[i] != b.data[i]) return false;
}
return true;
}
bool operator != (const Decimal &a, const Decimal &b) {
return !(a == b);
}
Decimal & Decimal::operator += (long long x) {
if (!is_neg) {
if (integer + x >= 0) {
integer += x;
} else {
bool last = false;
for (int i = len - 1; i >= 0; i--) {
if (last || data[i]) {
data[i] = mo - data[i] - last;
last = true;
} else {
last = false;
}
}
integer = -x - integer - last;
is_neg = true;
}
} else {
if (integer - x >= 0) {
integer -= x;
} else {
bool last = false;
for (int i = len - 1; i >= 0; i--) {
if (last || data[i]) {
data[i] = mo - data[i] - last;
last = true;
} else {
last = false;
}
}
integer = x - integer - last;
is_neg = false;
}
}
return *this;
}
Decimal & Decimal::operator += (int x) {
return *this += (long long)x;
}
Decimal & Decimal::operator -= (int x) {
return *this += (long long)-x;
}
Decimal & Decimal::operator -= (long long x) {
return *this += -x;
}
Decimal & Decimal::operator /= (int x) {
if (x < 0) {
is_neg ^= 1;
x = -x;
}
int last = integer % x;
integer /= x;
for (int i = 0; i < len; i++) {
long long tmp = 1LL * last * mo + data[i];
data[i] = tmp / x;
last = tmp - 1LL * data[i] * x;
}
if (is_neg && integer == 0) {
int i;
for (i = 0; i < len; i++) {
if (data[i] != 0) {
break;
}
}
if (i == len) {
is_neg = false;
}
}
return *this;
}
Decimal & Decimal::operator *= (int x) {
if (x < 0) {
is_neg ^= 1;
x = -x;
} else if (x == 0) {
init_zero();
return *this;
}
int last = 0;
for (int i = len - 1; i >= 0; i--) {
long long tmp = 1LL * data[i] * x + last;
last = tmp / mo;
data[i] = tmp - 1LL * last * mo;
}
integer = integer * x + last;
return *this;
}
Decimal operator - (const Decimal &a) {
Decimal ret = a;
// -0 = 0
if (!ret.is_neg && ret.integer == 0) {
int i;
for (i = 0; i < Decimal::len; i++) {
if (ret.data[i] != 0) break;
}
if (i < Decimal::len) {
ret.is_neg = true;
}
} else {
ret.is_neg ^= 1;
}
return ret;
}
Decimal operator + (const Decimal &a, int x) {
Decimal ret = a;
return ret += x;
}
Decimal operator + (int x, const Decimal &a) {
Decimal ret = a;
return ret += x;
}
Decimal operator + (const Decimal &a, long long x) {
Decimal ret = a;
return ret += x;
}
Decimal operator + (long long x, const Decimal &a) {
Decimal ret = a;
return ret += x;
}
Decimal operator - (const Decimal &a, int x) {
Decimal ret = a;
return ret -= x;
}
Decimal operator - (int x, const Decimal &a) {
return -(a - x);
}
Decimal operator - (const Decimal &a, long long x) {
Decimal ret = a;
return ret -= x;
}
Decimal operator - (long long x, const Decimal &a) {
return -(a - x);
}
Decimal operator * (const Decimal &a, int x) {
Decimal ret = a;
return ret *= x;
}
Decimal operator * (int x, const Decimal &a) {
Decimal ret = a;
return ret *= x;
}
Decimal operator / (const Decimal &a, int x) {
Decimal ret = a;
return ret /= x;
}
Decimal operator + (const Decimal &a, const Decimal &b) {
if (a.is_neg == b.is_neg) {
Decimal ret = a;
bool last = false;
for (int i = Decimal::len - 1; i >= 0; i--) {
ret.data[i] += b.data[i] + last;
if (ret.data[i] >= Decimal::mo) {
ret.data[i] -= Decimal::mo;
last = true;
} else {
last = false;
}
}
ret.integer += b.integer + last;
return ret;
} else if (!a.is_neg) {
// a - |b|
return a - -b;
} else {
// b - |a|
return b - -a;
}
}
Decimal operator - (const Decimal &a, const Decimal &b) {
if (!a.is_neg && !b.is_neg) {
if (a >= b) {
Decimal ret = a;
bool last = false;
for (int i = Decimal::len - 1; i >= 0; i--) {
ret.data[i] -= b.data[i] + last;
if (ret.data[i] < 0) {
ret.data[i] += Decimal::mo;
last = true;
} else {
last = false;
}
}
ret.integer -= b.integer + last;
return ret;
} else {
Decimal ret = b;
bool last = false;
for (int i = Decimal::len - 1; i >= 0; i--) {
ret.data[i] -= a.data[i] + last;
if (ret.data[i] < 0) {
ret.data[i] += Decimal::mo;
last = true;
} else {
last = false;
}
}
ret.integer -= a.integer + last;
ret.is_neg = true;
return ret;
}
} else if (a.is_neg && b.is_neg) {
// a - b = (-b) - (-a)
return -b - -a;
} else if (a.is_neg) {
// -|a| - b
return -(-a + b);
} else {
// a - -|b|
return a + -b;
}
}
Decimal operator + (const Decimal &a, double x) {
return a + Decimal(x);
}
Decimal operator + (double x, const Decimal &a) {
return Decimal(x) + a;
}
Decimal operator - (const Decimal &a, double x) {
return a - Decimal(x);
}
Decimal operator - (double x, const Decimal &a) {
return Decimal(x) - a;
}
Decimal & Decimal::operator += (double x) {
*this = *this + Decimal(x);
return *this;
}
Decimal & Decimal::operator -= (double x) {
*this = *this - Decimal(x);
return *this;
}
Decimal & Decimal::operator += (const Decimal &b) {
*this = *this + b;
return *this;
}
Decimal & Decimal::operator -= (const Decimal &b) {
*this = *this - b;
return *this;
}
// ---------- decimal lib end ----------
#define N 8005
int Fn,n,k,p,H,x,h[N],q[N],s[N];
Decimal predp[N],dp[N];
Decimal ans,g[N];
#define slope(i,j,k) (((i-k+1)*g[j].to_double()-(i-j+1)*g[k].to_double())/(k-j))
template<typename T>
void read(T &s)
{
s=0;
char ch=getchar();
while (!isdigit(ch))
ch=getchar();
while (isdigit(ch))
s=s*10+ch-'0',ch=getchar();
}
template<typename T>
void write(T s)
{
if (s>9)
write(s/10);
putchar(s%10+'0');
}
int main()
{
read(Fn),read(k),read(p);
read(H);
for (int i=2;i<=Fn;i++)
{
read(x);
if (x>H)
h[++n]=x;
}
k=min(k,n);
sort(h+1,h+n+1);
for (int i=1;i<=n;i++)
s[i]=s[i-1]+h[i];
for (int i=0;i<=n;i++)
dp[i]=H;
int lim=min(k,14);
int kz=n-k+lim;
for (int j=1;j<=lim;j++)
{
g[0]=dp[0];
predp[0]=dp[0];
for (int i=1;i<=kz;i++)
g[i]=dp[i]-s[i],predp[i]=dp[i];
int l=1,r=0;
q[++r]=0;
for (int i=1;i<=kz;i++)
{
while (l<r && slope(i,q[l],q[l+1])<=s[i])
l++;
int t=q[l];
dp[i]=(predp[t]+s[i]-s[t])/(i-t+1);
while (l<r && slope(i,q[r-1],q[r])>=slope(i,q[r],i))
r--;
q[++r]=i;
}
}
ans=dp[kz];
for (int i=kz+1;i<=n;i++)
ans=(ans+h[i])/2;
cout << ans.to_string(p+2) << endl;
return 0;
}