[AGC049E] Increment Decrement
题目大意:
我们可以对于一个非负整数序列进行如下两种操作任意次.
1.选定一项使其 $+1$ 或者 $−1$ , 会花费 $1$ 的代价.
2.选定一个区间使区间中的每一项 $+1$ 或者 $−1$ , 会花费 C 的代价.
定义一个非负整数序列的代价为从全 0 序列变为该序列所需的最小代价.
给定 $n$ 个序列 $B_i$ , 每个序列长为 $K$ , 依次将每个 $A_i$ 赋值为$B_{i,1},B_{i,2},\dots,B_{i,K}$ , 求所有 $K^n$ 个可能的 $A$ 序列代价和.
$n,c,k<=50$ $b_i<=10^9$
题解:
本题的关键点在于slope trick.
首先考虑一个序列$a$,我们如何进行dp使得花费最小呢.
因为有两种操作,我们不好直接维护,所以我们考虑分一下层.
假设我们已经做完了所有的$2$操作,得到一个序列$b$,我们考虑构造这样一个$b$.
最小化:
$$\sum\limits_{i=1}^{n}{\left\vert {a_i-b_i}\right\vert+C*max(0,b_{i+1}-b_i)}$$
直接dp,设$f_{i,j}$表示前$i$个位置,$b_i=j$的最小代价:
$$f_{i,j}=\min\limits_{k>=0}{\begin{Bmatrix}{f_{i-1,k}+C*max(j-k,0)}\end{Bmatrix}}+\left\vert {j-a_i}\right\vert$$
把$\left\vert {j-a_i}\right\vert$放在后面转移的时候再加:
$$f_{i,j}=\min\limits_{k>=0}{\begin{Bmatrix}{f_{i-1,k}+\left\vert{a_{i-1}-k}\right\vert+C*max(j-k,0)}\end{Bmatrix}}$$
初始化$f_{0,j}=C*max(j,0)$,答案是$f_{n+1,0}$.
下面证明$f_{i,j}$是下凸的,考虑归纳证明.首先$f_{1,j}$是下凸的.然后在原函数上加上$\left\vert{a_{i-1}-k}\right\vert$,一个下凸函数加上一个下凸函数还是一个下凸函数.接着设$g(x)=f_{i-1,x}+\left\vert{a_{i-1}-x}\right\vert,h(x)=C*max(x,0)$.就是对着$g,h$做一次$(min,+)$卷积,因为$g,h$都是下凸,所以做完得到的新函数还是下凸.
我们已经证明了$f_{i,j}$是下凸的了,所以我们就可以用slope trick来维护这个东西了.维护斜率变化的分界点.
因为我们求$f_{n+1,0}$,所以同时维护$v_0$的值.
显然斜率在$[0,C]$之间.
一开始我们往集合中加入$C个0$,表示$f_{1,j}$的函数.加入一个$\left\vert{a_{i-1}-k}\right\vert$相当于在$a_{i-1}$处加入两个点表示斜率在这里变化$2$,接着和h做$(min,+)$卷积,因为之前的加入使得最前面的一段斜率为$-1$,最后一段的斜率变成$C+1$也就是我们要把最大和最小的两个分界点弹出.
同时我们考虑$v0$的变化$v_0+a_{i-1}-min$,这个要在图像上画一下,相当于先把最前面一段往上掰$a_{i-1}$,再往下掰那个最小的分界点.
这样我们就模拟完了整个过程这样做的时间复杂度是$o(nlogn)$
我们考虑原问题,直接做是不好做的,但是我们发现对于一个序列$v0$会把所有的$a_{i}$都加上,然后减去了所有的最小的分界点.
所以我们考虑用这个序列的和减去弹出去的和.我们把原来的$b$矩阵离散,每次把大于等于$x$的$b_{i}$看成$1$,小于的看成$0$,算出$1$被弹出来的个数.
这样做一次我们就相当于求出了大于等于$x$的数被弹出来的个数,差分一下就知道了$b_i$被弹出的个数.
求个数的时候直接模拟上述slope trick过程,发现当维护分界点的可重集新加入一个绝对值后,$1$的个数达到$C+2$的时候一定会弹出$1$.
代码:

#include<bits/stdc++.h> #define mod 1000000007 using namespace std; int n,c,m; int pw[55],a[55][55],b[55][55],zhan[10005],cnt=0,ans=0; int f[55][55]; int solve(int x){ for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(b[i][j]>=x)a[i][j]=1; else a[i][j]=0; } } memset(f,0,sizeof(f)); f[0][0]=1; for(int i=1;i<=n;i++){ int c0=0,c1=0; for(int j=1;j<=m;j++){ if(a[i][j])c1++; else c0++; } for(int j=0;j<=c+2;j++){ int s=j,t=c+2-j; if(s&&t)s--; else if(s)s-=2; f[i][s+2]=(f[i][s+2]+1ll*f[i-1][j]*c1%mod)%mod; f[i][s]=(f[i][s]+1ll*f[i-1][j]*c0%mod)%mod; } } int res=1ll*pw[n]*n%mod; for(int i=1;i<=n;i++){ res=(res-1ll*f[i][c+2]*pw[n-i]%mod+mod)%mod; } return res; } int main(){ // freopen("[AGC049E].in","r",stdin); // freopen("[AGC049E].out","w",stdout); scanf("%d%d%d",&n,&c,&m); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ scanf("%d",&b[i][j]); zhan[++cnt]=b[i][j]; } } sort(zhan+1,zhan+1+cnt); cnt=unique(zhan+1,zhan+1+cnt)-zhan-1; pw[0]=1; for(int i=1;i<=n;i++)pw[i]=1ll*pw[i-1]*m%mod; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ ans=(ans+b[i][j])%mod; } } ans=1ll*ans*pw[n-1]%mod; int ss=0; for(int i=1;i<=cnt;i++){ ss=(ss+1ll*zhan[i]*(solve(zhan[i]+1)-solve(zhan[i])+mod)%mod)%mod; } printf("%d\n",(ans-ss+mod)%mod); return 0; }