CF1770F Koxia and Sequence(条件统计转组合数计数)

题意简述

给定 n,x,y,定义序列 {an} 合法当且仅当 i=1nai=xori=1n=y,你需要求出 a is validi=1nai 的值。

n<240,x<260,y<220

分析

第一步:先做一波非常重要的分析

答案要求所有合法序列的所有数的异或,而因为一个合法序列翻转后也是合法序列,因此两个方案的贡献被抵消。那么只有回文序列才有可能有贡献。

n 为偶数,那么因为回文,所以会两两抵消,因此 n 为偶数时答案为 0。若 n 为奇数,则除最中间的数以外全部抵消,为表述方便我们令那一项是 a1(你重排序列显然不会影响答案,实际上令啥或不令都无所谓),答案相当于求所有合法序列的 a1 的异或和。

第二步:考虑如何计数

考虑要让序列符合两个条件。若让序列满足条件二,那么很难做一个使得 a=x 的计数,所以考虑让 a=x,对条件二计数。

按位考虑 a1 每一位的贡献,由于是异或,我们只需要算出 a1 那一位为 1 的方案数的奇偶性,为 1 就有贡献。发现直接让或结果等于 y 很难做,考虑容斥,答案就是 yy(1)|y||y|f(y,i),因为只需要奇偶性,所以答案就是 yyf(y,i),其中 f(y,i) 表示当序列或为 ya1i 位是 1 的方案数。

第三步:求解 f(y,i)

引理:[xy]=(yx)mod2

证明:根据卢卡斯定理,(xy)(x2y2)(xmod2ymod2)(mod2),发现 (xmod2ymod2) 只有在 xmod2=0,ymod2=1 时为 0。而整个求解的过程就是二进制分解的过程,所以 (xy)1 就相当于对于每一位,x 为 0 时 y 就不能为 1,等价于 yx 子集。

f(y,i)=[2iy]a=x[2ia1]i[aiy](后文为方便把 [2iy] 省略),根据引理可以直接把原式子变成组合数式子(方便推导):

f(y,i)=a=x(a12i)i(yai)

考虑将 (a12i) 扔到外面,把右面的 (ya1) 分离出来,那么 (a12i)(ya1) 就是经典的组合恒等式,它等于 (y2i)(y2ia12i),这样 (y2i) 就可以扔出 sigma 了:

f(y,i)=(y2i)a=x(y2ia12i)(ya2)

引理:(范德蒙德卷积)i(nri)(ms+i)=(n+mr+s),证明用组合意义不难证明。注意这个 i 可以取组合数有定义下的任何值。

我们把它推广到多个组合数相乘的形式,就是 a1+a2++ak=x(b1a1)(b2a2)(bkak)=(ba),因为 ai 可以取组合数有定义下的任何值。

我们把原式后面那坨 sigma 用范德蒙德卷积的推广合并掉,就是:

f(y,i)=(y2i)(y2i+y+y+a12i+a2+)=(y2i)(ny2ix2i)

把组合数化回原来的条件形式,f(y,i) 就等于 [2iy][x2iny2i]O(1) 的。

再把它代回容斥式子,a1i 位为 1 的方案数就可求,然后答案也很好求,然后就做完了。枚举 y 的子集和答案的二进制位,时间复杂度 O(ylogy)

代码很好写。

int n,x,y;
bool In(int x,int y){return (x&y)==x;}
void solve_the_problem(){
	n=rd(),x=rd(),y=rd();
	if(n%2==0)return (void)P0;
	int ans=0;
	per(j,19,0){
		int ret=0;
		for(int i=y;i;i=(i-1)&y){
			if(In((1<<j),i)&&In(x-(1<<j),n*i-(1<<j)))ret^=1;
		}
		ans+=ret*(1<<j);
	}
	write(ans);
}

作者:dcytrl

出处:https://www.cnblogs.com/dcytrl/p/18295173

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   dcytrl  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示