蒟蒻同步赛选手
考试的时候手推式子推了半天,结果只写个个 O ( m n 2 ) \ O(mn^{2}) O(mn2),水了 30 \ 30 30
我们设 f i , j \ f_{i,j} fi,j是经过 i \ i i次洗牌第 j \ j j张牌的期望(从下往上), f a i , j , f b i , j \ fa_{i,j},fb_{i,j} fai,j,fbi,j是两堆牌经过 i \ i i次洗牌的 j \ j j张牌的期望,我们有:
设 A , B \ A,B A,B是两堆牌的高度。
f i , j = ∑ k = 1 j ( j − 1 k − 1 ) A k ‾ B j − k ‾ ( f a i − 1 , k ) + ( j − 1 k − 1 ) B k ‾ A j − k ‾ ( f b i − 1 , k ) n j ‾ f_{i,j}=\sum_{k=1}^{j} \frac{\binom{j-1}{k-1}A^{\underline{k}}B^{\underline{j-k}}(fa_{i-1,k})+\binom{j-1}{k-1}B^{\underline{k}}A^{\underline{j-k}}(fb_{i-1,k})}{n^{\underline{j}}} fi,j=k=1∑jnj(k−1j−1)AkBj−k(fai−1,k)+(k−1j−1)BkAj−k(fbi−1,k)
为甚会得到这个式子呢?
我们有以下分析:
先看这个图:
c \ c c的高度为 A \ A A, d \ d d的高度为 B \ B B
我们逐个分析:
先不考虑其顺序。
第一张牌:
- 如果我们取 c 1 \ c1 c1,显然 c 1 \ c1 c1的贡献为 A n c 1 \ \frac{A}{n}c1 nAc1
- 如果我们取 d 1 \ d1 d1,显然 d 1 \ d1 d1的贡献为 B n d 1 \ \frac{B}{n}d1 nBd1
第二张牌
- 如果我们取 c 1 \ c1 c1,贡献为 A B n ( n − 1 ) c 1 \ \frac{AB}{n(n-1)}c1 n(n−1)ABc1
- 如果我们取 d 1 \ d1 d1,贡献为 A B n ( n − 1 ) d 1 \ \frac{AB}{n(n-1)}d1 n(n−1)ABd1
- 如果我们取 c 2 \ c2 c2,贡献为 A ( A − 1 ) n ( n − 1 ) c 2 \ \frac{A(A-1)}{n(n-1)}c2 n(n−1)A(A−1)c2
- 如果我们取 d 2 \ d2 d2,贡献为 B ( B − 1 ) n ( n − 1 ) d 2 \ \frac{B(B-1)}{n(n-1)}d2 n(n−1)B(B−1)d2
⋯ \cdots ⋯
显然的,当我们第 j \ j j张选了 c k \ ck ck时,分子上肯定有 n n u m ‾ \ n^{\underline{num}} nnum,分子上肯定有 A k ‾ \ A^{\underline{k}} Ak和 B j − k ‾ \ B^{\underline{j-k}} Bj−k。对于 d k \ dk dk,我们有同样的分析。
那么考虑顺序的话是很么呢?显然是一个组合数 ( j − 1 k − 1 ) \ \binom{j-1}{k-1} (k−1j−1)。这个请读者自己分析。
致辞我们可以得到代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const long long mod=998244353;
//char buf[1<<20],*fs,*ft;
//inline char getc()
//{return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))? 0 : *fs++;}
inline long long read()
{long long s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
return s*w;}
//char buf1[1<<21],a1[20];int p11,p12=-1;
//inline void flush()
//{fwrite (buf1,1,p12+1,stdout),p12=-1;}
//inline void print(int x)
//{if(p12>1<<20) flush();if(x<0) buf1[++p12]=45,x=-x;
//do{a1[++p11]=x%10+48;}while(x/=10);
//do{buf1[++p12]=a1[p11];}while(--p11);buf1[++p12]='\n';}
/*
g the two line
f the c
frac n!
invf the inv of frac
a A
*/
long long n,m,a[100100],f[2][100100],frac[100100],invf[100100],g[1100][1100],type;
long long fa[100100],fb[100100];
inline long long poww(long long a,long long b)
{
long long res=1ll;
a%=mod;
while(b)
{
if(b&1)
{
res*=a;
res%=mod;
}
a*=a;
a%=mod;
b>>=1;
}
return res;
}
inline long long sum(int aa,int bb,int num,int k)
{
long long ans=invf[n]*frac[n-num-1];
ans%=mod;
ans*=g[k-1][num-(k-1)];
ans%=mod;
int aused1,bused1,aused2,bused2;
//ues in a
aused1=k-1ll;
bused1=num-(k-1ll);
//use in b
aused2=num-(k-1ll);
bused2=k-1ll;
long long suma=frac[aa]*invf[aa-aused1];
suma%=mod;
if((aused1<aa)&&(bused1<=bb))
{
suma*=frac[bb];
suma%=mod;
suma*=invf[bb-bused1];
suma%=mod;
suma*=aa-aused1;
suma%=mod;
suma*=fa[k];
suma%=mod;
}
else suma=0;
long long sumb=frac[bb]*invf[bb-bused2];
sumb%=mod;
if((bused2<bb)&&(aused2<=aa))
{
sumb*=frac[aa];
sumb%=mod;
sumb*=invf[aa-aused2];
sumb%=mod;
sumb*=bb-bused2;
sumb%=mod;
sumb*=fb[k];
sumb%=mod;
}
else sumb=0;
long long sum=suma+sumb;
sum%=mod;
ans*=sum%mod;
ans%=mod;
return ans;
}
/*
have got num cards and the next is the kth
*/
signed main()
{
// freopen("landlords.in","r",stdin);
// freopen("landlords.out","w",stdout);
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
n=read();
m=read();
type=read();
int i=1,j=1,k=1;
while(i<=m)
{
a[i]=read();
++i;
}
g[0][0]=1;
i=0;
while(i<=n)
{
j=0;
while(j<=n)
{
if(i) g[i][j]+=g[i-1][j];
g[i][j]%=mod;
if(j) g[i][j]+=g[i][j-1];
g[i][j]%=mod;
++j;
}
++i;
}
frac[0]=1;
i=1;
while(i<=n)
{
frac[i]=frac[i-1]*i;
frac[i]%=mod;
++i;
}
i=0;
while(i<=n)
{
invf[i]=poww(frac[i],mod-2);
invf[i]%=mod;
++i;
}
i=1;
while(i<=n)
{
f[0][n-i+1]=poww(i,type);
f[0][n-i+1]%=mod;
++i;
}
int cur=0;
i=1;
while(i<=m)
{
cur^=1;
int aa=0,bb=0;
j=n-a[i]+1;
memset(f[cur],0,sizeof(f[cur]));
memset(fa,0,sizeof(fa));
memset(fb,0,sizeof(fb));
while(j<=n)
{
fa[++aa]=f[cur^1][j];
++j;
}
j=1;
while(j<=n-a[i])
{
fb[++bb]=f[cur^1][j];
++j;
}
j=1;
while(j<=n)
{
k=1;
while(k<=min(j,max(aa,bb)))
{
f[cur][j]+=sum(aa,bb,j-1,k);
f[cur][j]%=mod;
++k;
}
++j;
}
++i;
}
int qq=read();
while(qq--)
{
int cc=read();
// while(f[cur][n-cc+1]<0) f[cur][n-cc+1]+=mod;
printf("%lld\n",f[cur][n-cc+1]);
}
return 0;
}
当然只有三十分, O ( m n 2 ) \ O(mn^{2}) O(mn2)的复杂度是很不优秀的。
我们经过打表证明发现在
f
(
i
)
=
i
\ f(i)=i
f(i)=i时答案是一个等差数列,那么我们就只需要记录前两项了。
证明:
我们思考一下这个过程:我们发现任意时刻我们的牌堆都是上面的牌没有下面的牌大的(不取膜)。我们挖掘这个东西的性质,发现它实际上是基数排序的逆过程。而且原序列的所有情况概率是相同的。
同理可得 f ( i ) = i 2 \ f(i)=i^{2} f(i)=i2的时候是二次函数
实际上我们不需要分情况讨论,一次函数本身就是特殊的二次函数。
此处我用拉格朗日插值就二次函数通项,大佬可以用二阶差分啥的。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const long long mod=998244353;
//char buf[1<<20],*fs,*ft;
//inline char getc()
//{return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))? 0 : *fs++;}
inline long long read()
{long long s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
return s*w;}
//char buf1[1<<21],a1[20];int p11,p12=-1;
//inline void flush()
//{fwrite (buf1,1,p12+1,stdout),p12=-1;}
//inline void print(int x)
//{if(p12>1<<20) flush();if(x<0) buf1[++p12]=45,x=-x;
//do{a1[++p11]=x%10+48;}while(x/=10);
//do{buf1[++p12]=a1[p11];}while(--p11);buf1[++p12]='\n';}
/*
g the two line
f the c
frac n!
invf the inv of frac
a A
*/
long long n,m,a[500500],f[2][5],frac[10001000],invf[10001000],type;
long long fa[5],fb[5];
long long g(long long i,long long j)
{
long long ans=frac[i+j];
ans*=invf[i];
ans%=mod;
ans*=invf[j];
ans%=mod;
return ans;
}
inline long long poww(long long a,long long b)
{
long long res=1ll;
a%=mod;
while(b)
{
if(b&1)
{
res*=a;
res%=mod;
}
a*=a;
a%=mod;
b>>=1;
}
return res;
}
inline long long sum(int aa,int bb,int num,int k)
{
long long ans=invf[n]*frac[n-num-1];
ans%=mod;
ans*=g(k-1,num-(k-1));
ans%=mod;
int aused1,bused1,aused2,bused2;
//ues in a
aused1=k-1ll;
bused1=num-(k-1ll);
//use in b
aused2=num-(k-1ll);
bused2=k-1ll;
long long suma=frac[aa]*invf[aa-aused1];
suma%=mod;
if((aused1<aa)&&(bused1<=bb))
{
suma*=frac[bb];
suma%=mod;
suma*=invf[bb-bused1];
suma%=mod;
suma*=aa-aused1;
suma%=mod;
suma*=fa[k];
suma%=mod;
}
else suma=0;
long long sumb=frac[bb]*invf[bb-bused2];
sumb%=mod;
if((bused2<bb)&&(aused2<=aa))
{
sumb*=frac[aa];
sumb%=mod;
sumb*=invf[aa-aused2];
sumb%=mod;
sumb*=bb-bused2;
sumb%=mod;
sumb*=fb[k];
sumb%=mod;
}
else sumb=0;
long long sum=suma+sumb;
sum%=mod;
ans*=sum%mod;
ans%=mod;
return ans;
}
/*
have got num cards and the next is the kth
*/
inline void getabc(long long x0,long long y0,long long x1,long long y1,long long x2,long long y2,long long &aa,long long &bb,long long &cc)
{
aa=0;
bb=0;
cc=0;
long long sum=0;
//0a 0b
sum=(x0-x1)*(x0-x2);
sum%=mod;
sum=poww(sum,mod-2);
sum*=y0;
sum%=mod;
aa+=sum;
aa%=mod;
sum*=(x1+x2);
sum%=mod;
bb+=sum;
bb%=mod;
//1a 1b
sum=(x1-x0)*(x1-x2);
sum%=mod;
sum=poww(sum,mod-2);
sum*=y1;
sum%=mod;
aa+=sum;
aa%=mod;
sum*=(x0+x2);
sum%=mod;
bb+=sum;
bb%=mod;
//2a 2b
sum=(x2-x0)*(x2-x1);
sum%=mod;
sum=poww(sum,mod-2);
sum*=y2;
sum%=mod;
aa+=sum;
aa%=mod;
sum*=(x0+x1);
sum%=mod;
bb+=sum;
bb%=mod;
bb*=-1;
bb%=mod;
if(bb<0) bb+=mod;
//0c
sum=(x0-x1)*(x0-x2);
sum%=mod;
sum=poww(sum,mod-2);
sum*=y0;
sum%=mod;
sum*=x1;
sum%=mod;
sum*=x2;
sum%=mod;
cc+=sum;
cc%=mod;
//1c
sum=(x1-x0)*(x1-x2);
sum%=mod;
sum=poww(sum,mod-2);
sum*=y1;
sum%=mod;
sum*=x0;
sum%=mod;
sum*=x2;
sum%=mod;
cc+=sum;
cc%=mod;
//2c
sum=(x2-x0)*(x2-x1);
sum%=mod;
sum=poww(sum,mod-2);
sum*=y2;
sum%=mod;
sum*=x0;
sum%=mod;
sum*=x1;
sum%=mod;
cc+=sum;
cc%=mod;
}
signed main()
{
// freopen("landlords2.in","r",stdin);
// freopen("landlords2.out","w",stdout);
memset(f,0,sizeof(f));
n=read();
m=read();
type=read();
int i=1,j=1,k=1;
while(i<=m)
{
a[i]=read();
++i;
}
frac[0]=1;
i=1;
while(i<=n)
{
frac[i]=frac[i-1]*i;
frac[i]%=mod;
++i;
}
invf[n]=poww(frac[n],mod-2);
i=n;
while(i>=1)
{
invf[i-1]=invf[i]*i;
invf[i-1]%=mod;
--i;
}
i=n;
while(i>=n-3)
{
f[0][n-i+1]=poww(i,type);
f[0][n-i+1]%=mod;
--i;
}
int cur=0;
i=1;
while(i<=m)
{
cur^=1;
int aa=0,bb=0;
memset(f[cur],0,sizeof(f[cur]));
memset(fa,0,sizeof(fa));
memset(fb,0,sizeof(fb));
j=1;
while(j<=3)
{
fb[++bb]=f[cur^1][j];
++j;
}
bb=n-a[i];
aa=a[i];
long long ax,bx,cx;
getabc(1,fb[1],2,fb[2],3,fb[3],ax,bx,cx);
int x=n-a[i]+1;
j=1;
while(j<=3)
{
fa[j]=ax*x;
fa[j]%=mod;
fa[j]*=x;
fa[j]%=mod;
fa[j]+=(bx*x)%mod;
fa[j]%=mod;
fa[j]+=cx;
fa[j]%=mod;
++x;
++j;
}
j=1;
while(j<=3)
{
k=1;
while(k<=3)
{
f[cur][j]+=sum(aa,bb,j-1,k);
f[cur][j]%=mod;
++k;
}
++j;
}
++i;
}
long long aaa,bbb,ccc;
getabc(1,f[cur][1],2,f[cur][2],3,f[cur][3],aaa,bbb,ccc);
int qq=read();
while(qq--)
{
int cc=read();
cc=n-cc+1;
int ans=0;
ans=aaa*cc;
ans%=mod;
ans*=cc;
ans%=mod;
ans+=(cc*bbb)%mod;
ans%=mod;
ans+=ccc;
ans%=mod;
if(ans<0) ans+=mod;
printf("%lld\n",ans);
}
return 0;
}
为京阿尼祈福 \ \color{white}\text{为京阿尼祈福} 为京阿尼祈福