Scaffolding
题解
考虑我们怎样放才是最优的放法。
很明显,当我们上楼后是不能带着材料下来的,所以我们带上楼的材料最多也只是刚好够放完上面一层,剩下的材料我们都在下一层中分完后再拿到上面去。
但我们该怎么分层呢?
很明显,对于一个完整的矩形我们是可以将它完整地分为一层的,我们对于这种完整的矩形我们在哪个位置上楼并不会影响我们的答案。
所以,我们要做的是将这样的柱状图分为多个完整的矩形处理。
观察到
n
⩽
1
0
5
n\leqslant 10^5
n⩽105,广义笛卡尔树的
O
(
n
2
)
O\left(n^2\right)
O(n2)暴力建图是明显不行的,考虑用
t
r
e
a
p
treap
treap的方法建图,这部分是
O
(
n
)
O\left(n\right)
O(n)的。
在建图的同时,我们可以在树上进行
d
p
dp
dp转移。
由于我们建完上面的楼后可能会剩一部分材料,而这部分材料只能拿到下面一层,所以我们肯定是从从叶子往根
d
p
dp
dp,某个节点有多的材料,人给它父亲。
我们记节点
i
i
i代表的矩阵的大小为
s
u
m
i
sum_{i}
sumi,
t
o
t
i
tot_{i}
toti表示
i
i
i的子树的总矩阵大小,
f
i
f_{i}
fi表示建完点
i
i
i的子树需要的最少运送次数,
g
i
g_{i}
gi表示建完
i
i
i的子树可以给它的祖先节点留下多少材料。
容易得到
d
p
dp
dp转移式:
f
u
=
∑
v
∈
s
o
n
u
f
v
+
max
(
0
,
⌈
s
u
m
u
−
∑
v
∈
s
o
n
u
g
v
m
⌉
)
f_{u}=\sum_{v\in son_{u}}f_{v}+\max(0,\left\lceil\frac{sum_{u}-\sum_{v\in son_{u}}g_{v}}{m}\right\rceil)
fu=v∈sonu∑fv+max(0,⌈msumu−∑v∈sonugv⌉)
g
u
=
m
f
u
−
t
o
t
u
g_{u}=mf_{u}-tot_{u}
gu=mfu−totu
这些转移都可以在遍历整棵树时进行,树在我们输入的时候就可以建好了。
时间复杂度 O ( n ) O\left(n\right) O(n)。
源码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 100005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
#define int LL
typedef unsigned long long uLL;
const int INF=0x3f3f3f3f;
const int mo=1e9+7;
const int inv2=499122177;
const int jzm=2333;
const int zero=10000;
const int lim=30000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-5;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int n,m,h[MAXN],lson[MAXN],rson[MAXN],sta[MAXN],siz[MAXN],stak;
LL f[MAXN],g[MAXN],sum[MAXN];
void sakura(int rt,int minn){
int sumg=0;siz[rt]++;
if(lson[rt])sakura(lson[rt],h[rt]),siz[rt]+=siz[lson[rt]],
f[rt]+=f[lson[rt]],sumg+=g[lson[rt]],sum[rt]+=sum[lson[rt]];
if(rson[rt])sakura(rson[rt],h[rt]),siz[rt]+=siz[rson[rt]],
f[rt]+=f[rson[rt]],sumg+=g[rson[rt]],sum[rt]+=sum[rson[rt]];
int tmp=siz[rt]*(h[rt]-minn)-sumg;sum[rt]+=(h[rt]-minn)*siz[rt];
if(tmp>0)f[rt]+=(tmp+m-1LL)/m;
g[rt]=1ll*m*f[rt]-sum[rt];
}
signed main(){
while(scanf("%lld %lld",&n,&m)!=EOF){
for(int i=1;i<=n;i++){
read(h[i]);bool flag=0;
while(stak&&h[sta[stak]]>h[i])stak--,flag=1;
if(sta[stak])lson[sta[stak]]=i;
if(flag)rson[i]=sta[stak+1];sta[++stak]=i;
}
sakura(sta[1],0);
printf("%lld\n",f[sta[1]]);stak=0;
for(int i=1;i<=n;i++)lson[i]=siz[i]=rson[i]=f[i]=g[i]=sum[i]=0;
}
return 0;
}