「EZEC」 Round 6 周年欢乐赛 div1-D 0-1 Trie
真的是高质量好题啊,让我这种组合数学不好的蒟蒻得到了锻炼。
我的做法比较生草:
考虑三个限制条件:
-
第一位必须是 \(0\)。
-
不能有 \(1\) 相邻。
-
最后一位必须是 \(1\)。
先强制钦定每个 \(1\) 前面有一个 \(0\),将它们捆绑在一起。那么我们现在有了 \(n\) 个 \(01\) 和 \(m-n\) 个 \(0\)。
此时无论怎么组合,前面两个条件就已经满足了。
以下我们称 含有 \(n\) 个 \(01\) 和 \(m\) 个 \(0\) 的集合 内部元素 通过排列组合得到的所有方案放到 trie 树上后的节点个数 为该集合的 贡献。
设 \(f_{n,m}\) 表示含有 \(n\) 个 \(01\) 和 \(m\) 个 \(0\) 的集合的 贡献。
首先有很明显的边界:
同时 \(f_{0,m},m>0\) 无定义,因为不满足第三条限制。
以及递推式:\(f_{n,m}=f_{n-1,m}+f_{n,m-1}+2,n>1,m>0\)。
那么我们现在就得到了 \(f\) 的一些值(下标从零开始):
为了方便,以下用 \((x,y)\) 对应 \(f\) 的二维矩阵中第 \(x\) 行第 \(y\) 列的元素。
考虑那个转移,实质上是:将一个要求的元素 \((x,y)\) 分裂成向上和向左的两个元素 \((x-1,y)\) 和 \((x,y-1)\),以及产生一个常数项贡献 \(2\)。
特殊的,第 \(1\) 行和第 \(0\) 列 在定义中 不能分裂。
不断进行上述过程,会得到若干个 \((1,i),i\in[1,m]\) 和 \((j,0),j\in[2,n]\) 这样的元素。
比较 Simple 的想法就是,直接枚举每个边界元素,计算其贡献,对于常数项系数 \(2\),其贡献发生在每一次分裂的过程中。而每次分裂会使得个数 \(+1\),那么就可以用总个数 \(sum\) 减 \(1\)(一开始的一个元素 \((n,m)\)),来获得 \(2\) 做贡献的次数。
这样就有 \(60\) 分的好成绩。
考虑到我们不能将贡献压缩到常数个点上的原因,就是这个边界上不能转移。
接下来,我们 强制 其转移,也就是将边界各往上往左扩展一格。为了符合转移式,第 \(0\) 行的元素 \((0,i),i>0\) 全部设为 \(-1\),第 \(-1\) 列的元素全部设为 \(0\)。然后我们就可以强制把上一步中 \((1,i),i\in (1,m]\) 的所有点往 \((1,1)\) 转移,同时过程中会产生若干个值为 \(-1\) 的元素。对于另一边也是一样的做法,可以将所有的贡献集中到 \((2,0)\) 这个元素上面。
然后对于 \(0\) 和 \(-1\) 的个数计算,都是形如 \(\sum_{i=0}^r \binom{n}{i}\) 的形式,可以直接将其写为 \(\binom{n+r+1}{r}\)。
那么这里就可以 \(\operatorname O(1)\) 求得,卢卡斯求组合数最多递归三层,所以是常数级别的(确信)。
以下包含了 \(30/60/100\) 的代码(前两部分注释掉了)
#include <stdio.h>
#define LL long long
using namespace std;
const int Rea=1e5+3;
struct Rin
{
char c;
inline char gc()
{
static char rea[Rea];
static char *head,*tail;
return head==tail&&(tail=(head=rea)+fread(rea,1,Rea,stdin),head==tail)?EOF:*head++;
}
inline Rin&operator >>(LL &x)
{
x=0;
bool tag=false;
for(c=gc();c>'9'||c<'0';c=gc())if(c=='-'){c=gc();tag=true;break;}
for(;c>='0'&&c<='9';c=gc())x=(x<<1)+(x<<3)+(c^'0');
if(tag)x=-x;
return *this;
}
inline Rin&operator >>(int &x)
{
x=0;
bool tag=false;
for(c=gc();c>'9'||c<'0';c=gc())if(c=='-'){c=gc();tag=true;break;}
for(;c>='0'&&c<='9';c=gc())x=(x<<1)+(x<<3)+(c^'0');
if(tag)x=-x;
return *this;
}
}rin;
/* const int M=1e3+3;
const int M=18888913;
int f[M][M];
inline int dfs(int n,int m)
{
if(n<0||m<0)return 0;
if(m==0)return (n<<1)%M;
if(f[n][m])return f[n][m];
if(n>1)f[n][m]+=2+dfs(n-1,m);
if(m)f[n][m]+=1+dfs(n,m-1)-(n>1);
f[n][m]%=M;
return f[n][m];
} */
const int M=18888913;
int sl[M];
int sr[M];
inline int prpr(int x,int y){return 1LL*x*y%M;}
inline int ksm(int x,int y){int ans=1;for(;y;y>>=1){if(y&1)ans=prpr(ans,x);x=prpr(x,x);}return ans;}
inline int C(LL a,LL b)
{
if(a<b)return 0;
if(a<M)return prpr(sl[a],prpr(sr[b],sr[a-b]));
return prpr(C(a/M,b/M),C(a%M,b%M));
}
inline int work()
{
// int n=rin(),m=rin();return dfs(n,m-n);
LL n,m;
rin>>n>>m;
if(n>m)return 0;m-=n;
if(n==1)return (m+2)%M;
if(m==0)return (n<<1)%M;
int ans=0;
int sum=0;
// for(int i=1;i<=m;i++)ans=(ans+prpr(i+2,C(n-2+m-i,n-2)))%M,sum=(sum+C(n-2+m-i,n-2))%M;
// for(int i=2;i<=n;i++)ans=(ans+prpr(i<<1,C(n-i+m-1,m-1)))%M,sum=(sum+C(n-i+m-1,m-1))%M;
// ans=(ans+prpr(sum-1,2))%M;
ans=C(n-1+m-1,m-1)*3+((C(n-2+m,m))<<2);
ans+=C(n+m-2,m-2);
sum+=C(m+n-2,n-3);
sum+=C(n-1+m-1,m-1);
sum+=C(n+m-2,m);
ans+=(sum-1<<1);
return (ans%M+M)%M;
}
int main()
{
int ans=0;
/* for(int i=1;i<M;i++)f[i][0]=i<<1;
for(int i=1;i<M;i++)f[1][i]=i+2;
for(int i=2;i<M;i++)for(int j=1;j<M;j++)f[i][j]=(f[i-1][j]+f[i][j-1]+2)%M; */
sl[0]=sr[0]=1;
for(int i=1;i<M;i++)sl[i]=prpr(sl[i-1],i);
sr[M-1]=ksm(sl[M-1],M-2);for(int i=M-2;i>=1;i--)sr[i]=prpr(sr[i+1],i+1);
int T;
for(rin>>T;T;T--)ans^=work();
printf("%d\n",ans);
return 0;
}

浙公网安备 33010602011771号