0308
T1
考虑从前往后一个一个填,如果要求 \(a_1 \sim a_n\) 是一个排列 ,那么这题就很简单了:填 \(a_i\) 的时候,我们根本不关心 \(a_1 \sim a_{i-1}\) 分别是多少,我们只需要关心 \(a_i\) 是前 \(i\) 个中第几大的,然后就可以直接 \(O(n)\) 算。但是 \(a_1\sim a_n\) 可以相同,就不能直接看出有多少小于 \(a_i\),于是考虑状压,\(f_{i,S}\) 表示前 \(i\) 个位置,若 \(S\) 的第 \(j\) 位是 \(0\),表示前 \(i\) 个数中第 \(j\) 小的和第 \(j+1\) 小的一样大,否则表示第 \(j\) 小的和 \(j+1\) 小的不一样大。然后枚举 \(a_{i+1}\) 插到哪个位置,就可以判断是否满足条件。这一步 dp 的复杂度是 \(O(2^n \times n^2)\)。
然后考虑统计答案,对于一个 \(S\) 我们可以数出 \(a_1 \sim a_n\) 中有多少不同的数,令其为 \(m\),插一下板,方案数就是 \(C_{k}^{m}\)(\(k\) 个值,分为 \(m+1\) 段,其中最后一段可以为空)。然后计算总和,就考虑每个差分的贡献,枚举差分的长度,第 \(i\) 个差分的贡献是 \((n-i+1) \times \sum_j j\times C_{k-j}^{m-1}\)。令 \(F_m(x)=\sum_i^x i \times C_{k-i}^{m-1}\),可以发现这是一个 \(m+1\) 次多项式,我们要求的是 \(F_m(k-m+1)\),所以就插值!!!吗?考虑到这个题它放在 T1,应该不会这么麻烦,仔细想一下:\(i \times C_{k-i}^{m-1}\) 的组合意义,其实可以看成是在一段长为 \(k-i\) 的区间选出 \(m-1\) 个位置,再在一段长度为 \(i\) 的区间选出 \(1\) 个位置,那么我们把这两个区间连起来,再在两个区间之间加一个位置,钦定它被选,就可以写出组合数:\(C_{k+1}^{m+1}\),可以发现这样的是一一对应的。总复杂度 \(O(2^n \times n^2)\)。
#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int mul(int a,int b){return 1ll*a*b%mod;}
int n,k;
int f[18][1<<17|5],g[20];
int inv[20];
inline int C(int a,int b){
if(a<b)return 0;
int res=1;
for(int i=1;i<=b;i++)res=mul(res,a-i+1),res=mul(res,inv[i]);
return res;
}
int main(){
freopen("function.in","r",stdin);
freopen("function.out","w",stdout);
cin>>n>>k;
if(n==1){
printf("%d %lld\n",k,1ll*k*(k+1)/2%mod);
return 0;
}
inv[1]=1;
for(int i=2;i<=19;i++)inv[i]=mul(inv[mod%i],mod-mod/i);
f[2][1]=1;
for(int i=3;i<=n;i++){
for(int s=0;s<1<<i-2;s++){
if(!f[i-1][s])continue;
for(int j=0;j<i;j++){
if(j+1>=i-1-(j+1)&&s>>j&1){
int t=s&(1<<j+1)-1|((s&~((1<<j+1)-1))<<1);
f[i][t]=add(f[i][t],f[i-1][s]);
t|=1<<j+1;
f[i][t]=add(f[i][t],f[i-1][s]);
}
}
f[i][s|1<<i-2]=add(f[i][s|1<<i-2],f[i-1][s]);
}
}
int ans1=0,ans2=0;
for(int s=0;s<1<<n-1;s++){
int cnt=0;
for(int i=0;i<n-1;i++)if(s>>i&1)cnt++;
if(cnt+1>k)continue;
ans1=add(ans1,mul(f[n][s],C(k,cnt+1)));
ans2=add(ans2,mul(f[n][s],mul(n,C(k+1,cnt+2))));
for(int i=0;i<n-1;i++)if(s>>i&1){
ans2=add(ans2,mul(f[n][s],mul(n-1-i,C(k+1,cnt+2))));
}
}
cout<<ans1<<endl<<ans2<<endl;
return 0;
}
T2
如果没有 \(a_i \le a_{i+1}\) 的限制,那么这就是一道普及组题:\(a_1 \sim a_{n-1}\) 随便填,\(a_n\) 填 \(Y \oplus a_1 \oplus \dots \oplus a_{m-1}\)。于是一共有 \(2^{m\times (n-1)}\) 个方案。
但是加上 \(a_i \le a_{i+1}\) 的限制,要求的就不是满足条件的序列数量,而是满足条件的可重集数量……
对于一个可重集,我们每次删除其中两个重复的数,然后最后会得到一个异或和依然为 \(Y\) 的不可重集。我们就统计这样的不可重集的方案数。
令 \(F_i\) 为大小为 \(i\),且异或和为 \(Y\) 的不可重集的个数。\(G_i\) 为长度为 \(i\),且异或和为 \(Y\) 的序列个数。有 \(G_i = 2^{m \times (i-1)}\)。
于是我们考虑容斥算出 \(F\),令 \(H_{i,j}\) 为将大小为 \(i\) 的不可重集加上 \(\frac {j-i} 2\) 对任意的数,然后随意排序,可以得到的序列数量。我们可以背包算出 \(H\)。然后容斥:\(F_i=\dfrac {G_i-\sum F_j \times H_{j,i}} {i!}\)。就可以算出所有 \(F\)。
然后我们在大小为 \(i\) 的不可重集上加入 \(\frac {n-i} 2\) 对任意的数,就可以唯一对应一个大小为 \(n\) 的可重集。这一步的方案数可以组合数来算。
于是就算出答案了,复杂度主要在求 \(H\) 的背包上,我场上的代码求了 \(O(n^2)\) 次背包,每次 \(O(n^3)\),总复杂度 \(O(n^5)\),但是细想一下可以对 \(i\) 相同的一起算,复杂度 \(O(n^4)\)。
以下是赛时代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=20,mod=998244353;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int mul(int a,int b){return 1ll*a*b%mod;}
inline int qpow(int a,ll b){
int c=1;
for(;b;b>>=1,a=mul(a,a))if(b&1)c=mul(c,a);
return c;
}
int n,num,tot,tot1;ll m;
int g[100],f[100];
int fac[100],ifac[100];
int h[100],h1[100];
int dp(int p,int q){
memset(h,0,sizeof(h));
h[0]=1;
for(int i=1;i<=p;i++){
for(int j=q;j>=0;j--){
for(int k=2;k<=j;k+=2){
h[j]=add(h[j],mul(h[j-k],ifac[k+1]));
}
}
}
int res=h[q];
for(int i=1;i<=q;i++){
for(int j=q;j>=0;j--)
for(int k=2;k<=j;k+=2)
h1[j]=add(h1[j],mul(h[j-k],ifac[k]));
for(int j=0;j<=q;j++){
h[j]=mul(h1[j],add(tot,mod-p-i+1));
h1[j]=0;
}
res=add(res,mul(h[q],ifac[i]));
}
res=mul(res,fac[p+q]);
return res;
}
inline int C(int a,int b){
int res=1;
for(int i=1;i<=b;i++)res=mul(res,a-i+1),res=mul(res,qpow(i,mod-2));
return res;
}
int dp2(int n){
if(!n)return 1;
int res=0;
for(int i=1;i<=n;i++){
res=add(res,mul(C(tot,i),C(n-1,i-1)));
}
return res;
}
int main(){
freopen("xor.in","r",stdin);
freopen("xor.out","w",stdout);
cin>>n>>m>>num;
m%=(mod-1);tot=qpow(2,m);
g[0]=!num;
for(int i=1;i<=n;i++)g[i]=qpow(2,1ll*m*(i-1)%(mod-1));
for(int i=0;i<=n;i++)f[i]=g[i];
fac[0]=1;
for(int i=1;i<=n;i++)fac[i]=mul(fac[i-1],i);
ifac[n]=qpow(fac[n],mod-2);
for(int i=n;i>=1;i--)ifac[i-1]=mul(ifac[i],i);
for(int i=0;i<=n;i++){
if(!f[i])continue;
f[i]=mul(f[i],ifac[i]);
for(int j=i+2;j<=n;j+=2){
f[j]=add(f[j],mod-mul(f[i],dp(i,j-i)));
}
}
int ans=0;
for(int i=n;i>=0;i-=2){
ans=add(ans,mul(f[i],dp2(n-i>>1)));
}
cout<<ans<<endl;
return 0;
}

浙公网安备 33010602011771号