Day 7
来验这套题的起因是考 chino 造的 Day4 的时候觉得坐牢了就往后看题,然后看到嘉然的两个数学题。觉得 T1 明显很不可做,T2 看起来很舒适就开 T2。为啥不开 T3?数据结构。
然后现在这俩数学题(大体上是)都基本整明白了,来写个题解。
原来的密码是 nsdddsyssymhyzzyfdykqxkfsjmxyx。可以手动拼读一下是什么意思。
T1
原题是 ABC288EX。然而数据史诗级加强。
首先令 \(M=M+1\),答案是
的 \(1-N\) 次项系数。\(x\) 乘法是加法卷积,\(z\) 乘法是异或卷积。
异或卷积考虑 FWT。先复习一下 FWT 干的是什么:\(z^i\) 项变成所有 \(\text{popcount}(i\wedge j)\equiv 0\pmod 2\) 的\(z^j\) 项减掉 \(\text{popcount}(i\wedge j)\equiv 1\pmod 2\) 的\(z^j\) 项。IFWT 就是所有项再乘个 \(2^{-n}\)。以下设上边那个运算为 \(i\otimes j\)。(\(n\) 是 \(M\) 的位长)
按照黎明前的巧克力那题的做法,手动展开 FWT:
然后分类讨论:
- 若 \(X\otimes i=0\):原式为
- 若 \(X\otimes i=1\):原式为
然后 IFWT 回去:
其中 \(cnt_i=\sum_{j=0}^{M-1}i\otimes j\)。
然后变成这么两个问题:
- 算 \(\left(\dfrac 1{1+x}\right)^a\left(\dfrac 1{1-x}\right)^b\):ODE 手动机一下,求导表示自己。设这个东西是 \(F\),那么
- 对于每个 \(i\) 算 \(cnt_i\) 和个数。诚实的说这个我不会,是贺的。
先考虑怎么算一个:若 \(i\) 的二进制最低位为 \(low\),枚举 \(j\) 最高的和 \(M\) 不同的位 \(d\),若 \(d>low\),则除了 \(low\) 别的可以随便选,用 \(low\) 一位调整使得 \(i\otimes j=1\),贡献是 \(2^{d-1}\)。如果 \(d\le low\),则由于 \(d\) 及以下 \(i\) 全是 \(0\),即已经确定了 \(i\otimes j\),可以根据 \(i\otimes M\) 算出。这也说明了 \(cnt_i\) 只有 \(O(n)\) 组。
然后我们就只需要算是 \(cnt_i\) 的 \(i\) 个数。这个可以数位 dp 一下:仍然外层枚举 \(low\)。设 \(dp_{x,0/1,0/1}\) 为 \(i\) 确定了 \(x\) 位及以上,第二维是 \(i\otimes M\),第三维是 \(i\otimes X\) 的方案。枚举一下三维和这一维的 \(i\) 是 \(0/1\) 就好了。
总复杂度 \(O(N\log M)\)。
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <map>
#define int long long
using namespace std;
const int mod=998244353;
int n,m,x,ans[100010],f[100010],inv[100010],dp[65][2][2],sum[2];
map<int,int>mp;
int qpow(int a,int b){
int ans=1;
while(b){
if(b&1)ans=1ll*ans*a%mod;
a=1ll*a*a%mod;
b>>=1;
}
return ans;
}
void solve(pair<int,int>p){
int cnt=p.second%mod,a=p.first%mod,b=(m-p.first)%mod;
f[0]=cnt;sum[0]=f[0];sum[1]=0;
for(int i=0;i<n;i++){
f[i+1]=((sum[0]+sum[1])%mod*b%mod-(sum[i&1]-sum[(i&1)^1]+mod)%mod*a%mod+mod)%mod*inv[i+1]%mod;
sum[(i&1)^1]=(sum[(i&1)^1]+f[i+1])%mod;
}
for(int i=0;i<=n;i++)ans[i]=(ans[i]+f[i])%mod;
}
signed main(){
scanf("%lld%lld%lld",&n,&m,&x);inv[1]=1;m++;
for(int i=2;i<=n;i++)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
solve(make_pair(0,1));
for(int i=0;i<=60;i++){
memset(dp,0,sizeof(dp));
dp[61][0][0]=1;
for(int j=60;j>=i;j--){
for(int b=0;b<2;b++){
for(int c=0;c<2;c++){
for(int w=(i==j);w<2;w++){
dp[j][b^(m>>j&w)][c^(x>>j&w)]=(dp[j][b^(m>>j&w)][c^(x>>j&w)]+dp[j+1][b][c])%mod;
}
}
}
}
mp.clear();
for(int b=0;b<2;b++){
for(int c=0;c<2;c++){
int cnt=0;
for(int j=60;j>i;j--)if(m>>j&1)cnt+=1ll<<(j-1);
for(int j=i;j>=0;j--)if((m>>j&1))if(b^(i==j))cnt+=1ll<<j;
if(c)mp[cnt]=(mp[cnt]-dp[i][b][c]+mod)%mod;
else mp[cnt]=(mp[cnt]+dp[i][b][c])%mod;
}
}
for(pair<int,int>p:mp)solve(p);
}
for(int i=1;i<=n;i++){
ans[i]=1ll*ans[i]*qpow((1ll<<61)%mod,mod-2)%mod;
printf("%lld\n",ans[i]);
}
return 0;
}
T2
\(O(n^3)\) 部分分:设 \(dp_{l,r,dep}\) 为当前笛卡尔树的区间是 \([l,r]\),深度为 \(dep\) 的方案数,转移随便写写(真的不想再说)。直接说正解是什么东西。
事实上 dp 为我们提供了一个方向:若 \(x\) 位置的数为 \(i\),则 \(x\) 节点一定满足两个条件:
- 深度 \(\le i\)。
- 子树大小 \(\le n-i+1\)。
容易发现这两个条件没有交集。于是用总数 \(Cat_n\) 减掉这两个就行。问题变成算 \(x\) 节点深度/子树大小为 \(i\) 的方案,然后后缀和一下。
先考虑算第二个:枚举左右子树 \(i,j\) 大小,总方案数是 \(\sum_{i=0}^{x-1}\sum_{j=0}^{n-x}Cat_iCat_j\)。外边还剩下 \(n-i-j-1\) 个点,那么确定了外边的二叉树之后一定有唯一的位置可以把 \(x\) 子树挂上去,因此方案还要乘个 \(Cat_{n-i-j-1}\)。
然后是第一个。仍然枚举 \(x\) 的祖先中有 \(i\) 个左边的,\(j\) 个右边的。那么首先上边顺序随意所以乘个 \(\dbinom{i+j}i\)。然后考虑整棵树的形态。先只看左边,右边是一样的。我们知道 \(n\) 个节点的二叉树和长 \(2n\) 的括号序列一一对应:往儿子走是左括号,走回来是右括号。那么如果 \(x\) 上边有 \(i\) 个点那么就代表着总长度为 \(2n-i\) 的括号序列,左括号比右括号多 \(i\) 个的方案数,这个方案格路计数一下就行了,是 \(\dbinom{2n-i}n-\dbinom{2n-i}{n+1}\)。
显然两个都可以跑 NTT。
T3
写的稀烂,我自己都看不懂。扔 latex 注释里了。