四维超立方体根本没见过,所以考虑通过低纬度向高一维度的图形推导得到规律
考虑一个正方形得到正方体的操作,就是俗话说的面动成体
得到的正方体有贡献的部分是原正方形,结束位置的正方形,和正方形划过部分的边缘
设(f[i][j])是在(i)维图形中,(j)维图形的数量
由于(i)维的图形是由(i-1)维图形移动得到的,那么开始和结束位置会有两个(i-1)维图形
移动过程中,(i-1)维图形中每个(j)维图形移动的痕迹会产生一个(j+1)维图形
所以得到
大力展开式子
我们发现系数其实是((2x+1)^n),答案是(x)的(m)次项系数
考虑如果有一种方法对(w)进行排序,使得:
对于(1le j<i),(w_i+w_jge m),或者对于(1le j<i),(w_i+w_j<m)
那么我们可以大力(dp):设(f[i][j])表示处理到第(i)个,(A)队中有(j)个元素的最大值
当情况(1)时,放入哪一队都能和另一队的所有元素搭配
(f[i][j]=max{f[i-1][j]+j,f[i-1][j-1]+i-j})
否则放入哪一队都没用
(f[i][j]=max{f[i-1][j],f[i-1][j-1]})
那么再考虑怎么让(w)排序成符合要求的亚子:
先将(w_i)升序排序
如果(w_1+w_nge m),那么把(w_n)放到当前序列最左侧,标记为(1),处理([1,n-1])
如果(w_1+w_n<m),那么把(w_n)放到当前序列最左侧,标记为(0),处理([2,n])
#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1)
#define lowbit(i) ((i)&(-i))
inline int read()
{
int x=0;char ch,f=1;
for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
if(ch=='-') f=0,ch=getchar();
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return f?x:-x;
}
const int N=2e3+10,p=1e9+7;
int n,m,ret,sum;
int a[N],w[N];
int g[N][N],f[N][N];
inline void main()
{
n=read(),m=read();
int head=1,tail=n;
for(int i=1;i<=n;++i) w[i]=read(); sort(w+1,w+n+1);
for(int i=n;i>=1;--i) (w[head]+w[tail]>=m)?a[i]=1,--tail:++head;
g[0][0]=1;
for(int i=1;i<=n;++i)
{
for(int j=0;j<=i;++j)
{
if(j^i)
{
int v=f[i-1][j]+(a[i]?j:0);
if(v==f[i][j]) (g[i][j]+=g[i-1][j])%=p;
if(v>f[i][j]) f[i][j]=v,g[i][j]=g[i-1][j];
}
if(j)
{
int v=f[i-1][j-1]+(a[i]?i-j:0);
if(v==f[i][j]) (g[i][j]+=g[i-1][j-1])%=p;
if(v>f[i][j]) f[i][j]=v,g[i][j]=g[i-1][j-1];
}
ret=max(ret,f[i][j]);
}
}
for(int i=0;i<=n;++i) sum=(sum+(f[n][i]==ret?g[n][i]:0))%p;
printf("%lld %lld
",ret,sum);
}
}
signed main()
{
red::main();
return 0;
}
/*
10 10
1 5 6 2 6 3 4 9 5 6
*/
我们先假设每张卡牌有标号,最后再除以(prodlimits_{i=1}^{m}a_i)
这东西看上去不能直接算,考虑容斥,设(f_i)是恰好有(i)对的情况,(g_i)是至少有(i)对的情况
那么显然
二项式反演得到
考虑求(g(x))
对于第(i)种卡片,我们强制至少有(x)对魔术对,问题等价于将(a_i)张卡片分成(a_i-x)个排列的方案数
考虑先算出全排列(a_i!),然后将整个数列用隔板法分组( binom{a_i-1}{a_i-x-1}),但是排列之间是没有顺序的,而隔板法分出来是有顺序的,所以除以((a_i-x)!)结果是
所以
此时(h_i(x))是分成(x)个排列的方案数
所有(h(x))乘起来得到(g(x)),同时确定最后排序之间的相对位置,答案乘上(j!)
#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
inline int read()
{
int x=0;char ch,f=1;
for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
if(ch=='-') f=0,ch=getchar();
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return f?x:-x;
}
const int N=1e5+10,p=998244353;
int n,m,k,ret;
int a[N],sum[N];
int fac[N],inv[N];
int A[N<<2],B[N<<2];
vector<int> st[N],g;
int pos[N<<2];
inline int fast(int x,int k)
{
int ret=1;
while(k)
{
if(k&1) ret=ret*x%p;
x=x*x%p;
k>>=1;
}
return ret;
}
inline int C(int n,int m)
{
if(n<m) return 0;
return fac[n]*inv[m]%p*inv[n-m]%p;
}
inline void ntt(int limit,int *a,int inv)
{
for(int i=0;i<limit;++i)
if(i<pos[i]) swap(a[i],a[pos[i]]);
for(int mid=1;mid<limit;mid<<=1)
{
int Wn=fast(3,(p-1)/(mid<<1));
for(int r=mid<<1,j=0;j<limit;j+=r)
{
int w=1;
for(int k=0;k<mid;++k,w=w*Wn%p)
{
int x=a[j+k],y=w*a[j+k+mid]%p;
a[j+k]=x+y;
if(a[j+k]>=p) a[j+k]-=p;
a[j+k+mid]=x-y;
if(a[j+k+mid]<0) a[j+k+mid]+=p;
}
}
}
if(inv) return;
inv=fast(limit,p-2);reverse(a+1,a+limit);
for(int i=0;i<limit;++i) a[i]=a[i]*inv%p;
}
inline int check(int tl,int tr)
{
int l=tl,r=tr,ret=tl;
while(l<=r)
{
int mid=(l+r)>>1;
if(sum[mid]-sum[tl-1]<=sum[tr]-sum[mid-1]) ret=mid,l=mid+1;
else r=mid-1;
}
return ret;
}
inline vector<int> solve(int l,int r)
{
if(l==r) return st[l];
int mid=check(l,r),tot=sum[r]-sum[l-1];
vector<int> ansl=solve(l,mid),ansr=solve(mid+1,r),ans;
ans.clear();
int limit=1,len=0;
while(limit<=tot) limit<<=1,++len;
for(int i=0;i<limit;++i) pos[i]=(pos[i>>1]>>1)|((i&1)<<(len-1));
for(int i=0;i<(int)ansl.size();++i) A[i]=ansl[i];
for(int i=ansl.size();i<limit;++i) A[i]=0;
for(int i=0;i<(int)ansr.size();++i) B[i]=ansr[i];
for(int i=ansr.size();i<limit;++i) B[i]=0;
ntt(limit,A,1),ntt(limit,B,1);
for(int i=0;i<limit;++i) A[i]=A[i]*B[i]%p;
ntt(limit,A,0);
for(int i=0;i<=tot;++i) ans.push_back(A[i]);
return ans;
}
inline void main()
{
m=read(),n=read(),k=read();
for(int i=fac[0]=1;i<=n;++i) fac[i]=fac[i-1]*i%p;
inv[n]=fast(fac[n],p-2);
for(int i=n-1;~i;--i) inv[i]=inv[i+1]*(i+1)%p;
for(int i=1;i<=m;++i) a[i]=read();
sort(a+1,a+m+1);
for(int i=1;i<=m;++i)
{
st[i].push_back(0);
for(int j=1;j<=a[i];++j)
{
st[i].push_back(C(a[i]-1,j-1)*fac[a[i]]%p*inv[j]%p);
}
}
for(int i=1;i<=m;++i) sum[i]=sum[i-1]+a[i];
g=solve(1,m);
for(int i=0;i<=n;++i) g[i]=g[i]*fac[i]%p;
for(int i=0;i<=n-k;++i)
{
if((k-(n-i))&1) (ret-=C(n-i,k)*g[i])%=p;
else (ret+=C(n-i,k)*g[i])%=p;
}
ret=(ret+p)%p;
for(int i=1;i<=m;++i) ret=ret*inv[a[i]]%p;
printf("%lld
",ret);
}
}
signed main()
{
red::main();
return 0;
}