【洛谷】P8367 [LNOI2022] 盒
题意
数轴上有 \(n\) 个位置,第 \(i\) 个位置有 \(a_i\) 个球,将一个球从 \(i\) 移动到 \(i+1\) 或从 \(i+1\) 移动到 \(i\) 需要花费 \(w_i\) 的代价。
对于所有的结果数量序列 \(b\),满足 \(\sum_{i=1}^{n}a_i=\sum_{i=1}^{n}b_i=S\)。计算 \(a\) 变成 \(b\) 的最小代价之和。
\(1 \leq n \leq 5 \times 10^5,1 \leq S \leq 2 \times 10^6\)。
思路:
先考虑给定 \(b\) 序列的情况如何计算答案。
令 \(Sa\) 和 \(Sb\) 分别表示 \(a,b\) 序列的前缀和。单独计算每条边对于答案的贡献,对于 \(i \sim i+1\) 这条边,在最优方案下通过它的次数为 \(|Sa_i - Sb_i|\),那么最终的答案就是:
而题意是要求所有结果序列 \(b\) 的最小代价之和,那么此时就可以枚举 \(Sb_i=j\),由于 \(\sum_{i=1}^{n}b_i=S\)。就有 \(\sum_{k=1}^{i} b_k=j,\sum_{k=i+1}^{n} b_k=S-j\)。题目中要求 \(b_i \geq 0\),那么就等价于求不定方程的非负整数解的个数,利用插板法可以得到答案的表达式:
考虑去掉绝对值,对于 \(j \leq Sa_i\) 的部分,内层循环可以拆成:
令:
那么这一部分的答案转化为:
对于 \(j \geq Sa_i\) 的部分同理,得到最终答案的表达式:
考虑 \(f(n,S,i,k)\) 的组合意义:一共有 \(n\) 个盒子,在前 \(i\) 个盒子中放入了 \(\leq k\) 个球,且总的球数为 \(S\),的方案数。
那么也就等价于第 \(k+1\) 个球不在前 \(i\) 个盒子中,考虑枚举第 \(k+1\) 个球所在的盒子,得到:
利用类似于吸收恒等式的方法,可以进行如下转换:
证明只需要将组合数展开,带入\(g(n,S,i,k)\),可以得到:
注意到当 \(k=0\) 时没有意义,将 \(j\) 用 \(j-1\) 替换掉,可以得到:
那么只需要分别维护四个 \(f(n,S,i,k)\),移动 \(k\) 的时候用到 \(f(n,S,i,k)\) 的第一个表达式,移动 \(i\) 个时候用到 \(f(n,S,i,k)\) 的第二个表达式。
由于 \(i\) 和 \(k\) 单调不降,所以总的移动次数为 \(O(n+S)\)。
code:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=3e6+10,mod=998244353;
int n,m,fac[N],infac[N],inv[N],a[N],w[N],s[N];
int read()
{
int res=0,f=1;char c=getchar();
while(c<'0'||c>'9') ((c=='-')&&(f=-f,0)),c=getchar();
while(c>='0'&&c<='9') res=(res<<1)+(res<<3)+c-'0',c=getchar();
return res;
}
void init()
{
fac[0]=fac[1]=infac[0]=infac[1]=inv[0]=inv[1]=1;
for(int i=2;i<N;i++) fac[i]=1ll*fac[i-1]*i%mod;
for(int i=2;i<N;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
for(int i=2;i<N;i++) infac[i]=1ll*infac[i-1]*inv[i]%mod;
}
int C(int n,int m){if(n<0||m<0||n<m) return 0;return 1ll*fac[n]*infac[m]%mod*infac[n-m]%mod;}
void Add(int &a,int b){a+=b;if(a>=mod) a-=mod;}
void Sub(int &a,int b){a-=b;if(a<0) a+=mod;}
struct binom
{
int n,S,i,k,res;
void print(){printf("%d %d %d %d %d\n",n,S,i,k,res);}
void init(int n_,int S_,int i_,int k_)
{
n=n_,S=S_,i=i_,k=k_;res=0;
for(int j=0;j<=k;j++) Add(res,1ll*C(j+i-1,i-1)*C(S-j+n-i-1,n-i-1)%mod);
}
void movei(int new_i)
{
for(int j=i+1;j<=new_i;j++) Sub(res,1ll*C(j+k-1,j-1)*C(n-j+S-(k+1),n-j)%mod);i=new_i;
}
void movek(int new_k)
{
for(int j=k+1;j<=new_k;j++) Add(res,1ll*C(j+i-1,i-1)*C(S-j+n-i-1,n-i-1)%mod);k=new_k;
}
}f1,f2,f3,f4;
//g(n,S,i,S)-Sa_i*f(n,S,i,S)+2Sa_i*f(n,S,i,Sa_i)-2g(n,S,i,Sa_i)
//f1=f(n+1,S-1,i+1,S-1),f2=f(n,S,i,S),f3=f(n,S,i,Sa_i),f4=f(n+1,S-1,i+1,Sa_i-1)
void solve()
{
n=read();for(int i=1;i<=n;i++) a[i]=read(),s[i]=s[i-1]+a[i];int S=s[n],ans=0;
// f3.init(2,5,0,2);printf("%d\n",f3.res);f3.movei(1);printf("%d\n",f3.res);
f1.init(n+1,S-1,1,S-1);f2.init(n,S,1,S);f3.init(n,S,1,0);f4.init(n+1,S-1,1,-1);
// f3.init(2,5,0,0);f3.movei(1);f3.print();
for(int i=1;i<n;i++)
{
int res=0;
f1.movei(i+1);f2.movei(i);f3.movei(i);f4.movei(i+1);
f3.movek(s[i]);f4.movek(s[i]-1);
// printf("%d %d %d %d\n",f1.res,f2.res,f3.res,f4.res);
// printf("%d %d %d %d\n",f3.n,f3.S,f3.i,f3.k);
Add(res,1ll*i*f1.res%mod);Sub(res,1ll*s[i]*f2.res%mod);
Add(res,2ll*s[i]*f3.res%mod);Sub(res,2ll*i*f4.res%mod);
Add(ans,1ll*res*read()%mod);
}
printf("%d\n",ans);
}
int main()
{
init();int T;scanf("%d",&T);while(T--) solve();
return 0;
}

浙公网安备 33010602011771号