正题
题目链接:https://www.luogu.com.cn/problem/AT2070
题目大意
有三堆卡牌各有n,m,kn,m,kn,m,k张,每张上写了a/b/ca/b/ca/b/c,对于第1/2/31/2/31/2/3堆卡牌。然后开始从第一堆拿牌,然后根据拿到的牌在对应的堆拿牌。
如果到一堆拿牌时没有牌就结束,求第一张牌结束的方案数。
1≤n,m,k≤3×1051\leq n,m,k\leq 3\times 10^51≤n,m,k≤3×105
解题思路
显然牌的序列我们不是很好处理因为不是顺序拿的,我们可以操作每次取的堆的编号序列。
显然它的长度不是固定的,我们枚举其长度n+in+in+i(也就是除了nnn个111以外有iii个其他的)。
然后答案就是
∑i=0m+k3m+k−i(n+i−1i)∑i−k≤j≤m(ij)\sum_{i=0}^{m+k}3^{m+k-i}\binom{n+i-1}{i}\sum_{i-k\leq j\leq m}\binom{i}{j}i=0∑m+k3m+k−i(in+i−1)i−k≤j≤m∑(ji)
后面那个很难处理但是注意到区间的范围是每次向前移动,而且上面那个值是每次加一,暴力拆开
∑i−k≤j≤m(ij)=∑i−k≤j≤m(i−1j−1)+(i−1j)\sum_{i-k\leq j\leq m}\binom{i}{j}=\sum_{i-k\leq j\leq m}\binom{i-1}{j-1}+\binom{i-1}{j}i−k≤j≤m∑(ji)=i−k≤j≤m∑(j−1i−1)+(ji−1)
⇒2∑i−1−k≤j≤m(i−1j)−(i−1i−1−k)−(i−1m)\Rightarrow 2\sum_{i-1-k\leq j\leq m}\binom{i-1}{j}-\binom{i-1}{i-1-k}-\binom{i-1}{m}⇒2i−1−k≤j≤m∑(ji−1)−(i−1−ki−1)−(mi−1)
然后就可以O(n)O(n)O(n)递推了。
时间复杂度:O(n+m+k)O(n+m+k)O(n+m+k)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=1e6+10,P=1e9+7;
ll n,m,k,ans,fac[N],inv[N],s[N],pw[N];
ll C(ll n,ll m)
{return fac[n]*inv[m]%P*inv[n-m]%P;}
signed main()
{fac[0]=inv[0]=inv[1]=pw[0]=1;for(ll i=1;i<N;i++)pw[i]=pw[i-1]*3ll%P;for(ll i=2;i<N;i++)inv[i]=P-inv[P%i]*(P/i)%P;for(ll i=1;i<N;i++)fac[i]=fac[i-1]*i%P,inv[i]=inv[i-1]*inv[i]%P;scanf("%lld%lld%lld",&n,&m,&k);s[0]=1;ans=pw[m+k];for(ll i=1;i<=m+k;i++){s[i]=2*s[i-1]%P;if(i>m)(s[i]+=P-C(i-1,m))%=P;if(i>k)(s[i]+=P-C(i-1,i-k-1))%=P;(ans+=C(n+i-1,i)*s[i]%P*pw[m+k-i])%=P;}printf("%lld\n",ans);return 0;
}