ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[LNOI2022]盒

2022-06-08 11:33:40  阅读:193  来源: 互联网

标签:LNOI2022 int sum MAXN fac binom inv


\(LNOI2022\)盒

由于是加的形式,那么可以套路的拆贡献,枚举每条边的贡献就好了

\(40pts\)

//比较显然的事情
//首先确定了一个B数组之后
//最小的移动应该是
//设左右两侧比原先值多的为Max
//少的为Min
///我们考虑每个点只计算向一侧的贡献
//我们的答案是(Max-Limx)*val+(Limn-Min)*val
//n^2的dp很好给出
//dp[i][j]表示前i个选了j个的答案
//不需要貌似,只需要枚举这个点选的,和左边选的,还有右边选的就好了
//枚举这个点选的,枚举左边,复杂度n^2
#include<bits/stdc++.h>
#define int long long
#define MAXN 20005
using namespace std;
const int mod=998244353;
int n,w[MAXN],a[MAXN],Sum[MAXN];
int fac[MAXN+5],inv[MAXN+5];
void Init()
{
	fac[0]=inv[0]=1;
	fac[1]=inv[1]=1;
	for(int i=2;i<=MAXN;i++)
	{
		fac[i]=(fac[i-1]*i)%mod;
		inv[i]=(mod-mod/i)*inv[mod%i]%mod;
	}
	for(int i=1;i<=MAXN;i++)
	{
		inv[i]=(inv[i]*inv[i-1])%mod;
	}	
}
int C(int n,int m)
{
	return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
void sol()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
    {
    	scanf("%lld",&a[i]);
    	Sum[i]=Sum[i-1]+a[i];
	}
	for(int i=1;i<n;i++)
	{
		scanf("%lld",&w[i]);
	}
	int res=0;
	for(int i=1;i<n;i++)
	{
		for(int bed=0;bed<=Sum[n];bed++)
		{
			int pre=Sum[n]-bed;
			{
			     res+=(abs(Sum[i]-pre))*w[i]%mod*C(pre+i-1,i-1)%mod*C(bed+(n-i)-1,n-i-1)%mod;
				 res%=mod;
			}
		}
	}
	cout<<res<<"\n";
}
int T;
signed main()
{
	scanf("%lld",&T);
	Init();
	while(T--) sol();
}

答案为

\[\sum_i w[i]\sum_{j=0}^S|s_i-j|\binom{j+i-1}{i-1}\binom{S-j+n-i-1}{n-i-1} \\ \sum_iw[i](2\times\sum_{j=0}^{s[i]}(s_i-j)\binom{j+i-1}{i-1}\binom{S-j+n-i-1}{n-i-1}+\sum_{j=0}^{S}(j-s_i)\binom{j+i-1}{i-1}\binom{S-j+n-i-1}{n-i-1}) \]

考虑后面那个式子

\[\sum_{j=0}^S(j-s_i)\binom{j+i-1}{i-1}\binom{S-j+n-i-1}{n-i-1} \\ \sum_{j=0}^S j\binom{j+i-1}{i-1}\binom{S-j+n-i-1}{n-i-1}-\sum_{j=0}^S s_i\binom{j+i-1}{i-1}\binom{S-j+n-i-1}{n-i-1} \\ \sum_{j=0}^S j\binom{j+i-1}{j}\binom{S-j+n-i-1}{n-i-1}-\sum_{j=0}^S s_i\binom{j+i-1}{j}\binom{S-j+n-i-1}{n-i-1} \\ i\sum_{j=0}^S\binom{j+i-1}{i}\binom{S-j+n-i-1}{n-i-1}-\sum_{j=0}^S s_i\binom{j+i-1}{j}\binom{S-j+n-i-1}{n-i-1} \\ \]

前半部分\(j\leftarrow j-1\)

\[i\sum_{j=0}^{S-1}\binom{j+i}{i}\binom{S-j+n-i-2}{n-i-1}-\sum_{j=0}^S s_i\binom{j+i-1}{j}\binom{S-j+n-i-1}{n-i-1} \\ i\sum_{j=0}^{S-1}\binom{j+i}{j}\binom{S-j+n-i-2}{S-j-1}-\sum_{j=0}^S s_i\binom{j+i-1}{j}\binom{S-j+n-i-1}{n-i-1} \]

\[\binom{n+m}{n}=\sum_{j=0}^{m}\binom{i+j}{j}\binom{n+m-i-j-1}{m-j} \]

替换原式

\[i\sum_{j=0}^{S-1}\binom{j+i}{j}\binom{n+S-1-i-j-1}{S-1-j}-\sum_{j=0}^S s_i\binom{j+i-1}{j}\binom{(n-1)-(i-1)+S-j-1}{S-j} \\ i\binom{n+S-1}{n}-s_i\binom{n+S-1}{S} \]

那么对于前一个式子,我们变化的只是枚举上界,考虑组合意义变化为,在第 \(i\) 列的纵坐标不能超过 \(s[i]\)

等价于在走到某一行\(/\)列,对应的列\(/\)行不能超过某个数

在第 \(p\) 列 \(y\) 不超过第 \(q\) 行的方案数,等价于第 \(q\) 行到第 \(q+1\) 行,\(x\) 至少为 \(p+1\) 的方案数

\[\sum_{i=0}^q\binom{p+i}{i}\binom{n+m-p-i-1}{m-i}=\sum_{i=p+1}^{n}\binom{i+q}{q}\binom{n+m-i-q-1}{n-i} \]

我们可以\(O(1)\)的更新值的变化,从而做到线性

#include<bits/stdc++.h>
#define int long long
#define MAXN 3000005
using namespace std;
const int N=3e6+100;
const int INF=LLONG_MAX,mod=998244353;
int my_pow(int a,int b)
{
	int res=1;
	while(b)
	{
		if(b&1) res=(res*a)%mod;
		a=(a*a)%mod;
		b>>=1;
	}
	return res;
}
int n,fac[MAXN+5],inv[MAXN+5],Sum[MAXN],w[MAXN],S;
void Init()
{
	 fac[0]=inv[0]=1;
	 fac[1]=inv[1]=1;
	 for(int i=2;i<=MAXN;i++)
	 {
	     fac[i]=(fac[i-1]*i)%mod;
	  	 inv[i]=(mod-mod/i)*inv[mod%i]%mod;
	 }
	 for(int i=1;i<=MAXN;i++)
	 {
	 	 inv[i]=(inv[i-1]*inv[i])%mod;
	 }
}
int C(int n,int m)
{
	return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
struct Solve
{
	  int n,m,p,q,res;
	  void Init(int N,int M)
	  {
		   n=N;m=M;p=0;q=0;
		   res=C(n+m-1,m);
		   return;
	  }
	  int move(int P,int Q)
	  {
	      while(q<Q)
		  {
	         ++q;
	      	 res+=C(q+p,q)*C(n+m-q-p-1,m-q)%mod;
	      	 res%=mod;
	      }
		  while(p<P)
		  {
		     ++p;
		     res-=C(p+q,p)*C(n+m-p-q-1,n-p)%mod;
		     res=(res%mod+mod)%mod;
		  }
	      return res;
	  }
}res1,res2;
void sol()
{
     cin>>n;
     for(int i=1,a;i<=n;i++)
     {
     	 cin>>a;
     	 Sum[i]=Sum[i-1]+a;
	 }
     for(int i=1;i<n;i++) cin>>w[i];  
     S=Sum[n];
     res1.Init(n-1,S);
  	 res2.Init(n,S-1);
  	 int Ans=0;
  	 for(int i=1;i<n;i++)
	 {
         int res=0;
	     res=(res+i*C(n+S-1,n))%mod;    
	     res=(res+mod-Sum[i]*C(n+S-1,S)%mod)%mod;
	     if(Sum[i]) res=(res+2*Sum[i]*res1.move(i-1,Sum[i]))%mod;
	     if(Sum[i]) res=(res+mod-2*i*res2.move(i,Sum[i]-1)%mod)%mod;
	     Ans=(Ans+res*w[i])%mod;
     }
  	 printf("%lld\n",Ans);
}
int T;
signed main()
{
     Init();
     cin>>T;
     while(T--) sol();
     return 0;
}

标签:LNOI2022,int,sum,MAXN,fac,binom,inv
来源: https://www.cnblogs.com/Eternal-Battle/p/16355060.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有