453D - Little Pony and Elements of Harmony (FWT + FWT 对模数无逆元逆变换的处理)

在这里插入图片描述
分析:要从 e i [ u ] = ∑ e ( i − 1 ) [ u ] ∗ b [ f ( u , v ) ] ei[u] = \sum e(i-1)[u] * b[f(u,v)] ei[u]=e(i1)[u]b[f(u,v)]入手,将式子写得简单点,也就是:
f [ i ] = ∑ f [ j ] ∗ b [ c [ i ⨁ j ] ] f[i] = \sum f[j] * b[c[i\bigoplus j]] f[i]=f[j]b[c[ij]],令 d [ i ] d[i] d[i] = b[c[i]]。
则有 f [ i ] = ∑ f [ j ] ∗ d [ i ⨁ j ] f[i] = \sum f[j] * d[i\bigoplus j] f[i]=f[j]d[ij],很明显是一个卷积,这意味着可以在 n ∗ l o g ( n ) n*log(n) nlog(n)的时间内快速计算出所有的 f [ i ] f[i] f[i],即 e i ei ei
由于要卷t次,还要套一个快速幂,总体复杂度为 O ( n ∗ l o g ( n ) ∗ l o g ( t ) ) O(n*log(n)*log(t)) O(nlog(n)log(t))

这题特殊的地方在于模数不是质数,那么FWT在逆变换时可能得不到逆元,因此要用另外一种处理:将模数乘上长度,逆变换时就是在正变换的基础上每一项除以长度,由于模数变得很大,在快速幂和点值相乘时就需要用到黑科技o(1)快速乘。
(虽然不知道为什么可以这样搞,但是好像逆变化都可以这样搞,而且逆变化的样式和原来不同了,先记着吧,待补)

(用dp的方法得到 C 数组也有点妙啊)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e6 + 10;
ll m,t,p;
ll mul(ll x,ll y,ll MOD) {
	ll res=x*y-(ll)((long double)x/MOD*y+0.1)*MOD;
	return (res%MOD+MOD)%MOD;
}
ll a[maxn],b[maxn],c[maxn],d[maxn];
void fwt(ll a[],int len,int f) {
	for(int s = 2; s <= len; s <<= 1) {
		for(int j = 0; j < len; j += s) {
			for(int k = 0; k < s / 2; k++) {
				ll x = a[j + k],y = a[j + k + s / 2];
				a[j + k] = (x + y) % p;
				a[j + k + s / 2] = (x - y + p) % p; 
			}
		}
	}
	if(f == -1) 
		for(int i = 0; i < len; i++)
			a[i] /= len;
}
ll fpow(ll a,ll b,ll mod) {
	ll r = 1;
	while(b) {
		if(b & 1) r = mul(r,a,mod);
		a = mul(a,a,mod);
		b >>= 1;
	}
	return r % mod;
}
int main() {
	scanf("%lld%lld%lld",&m,&t,&p);
	ll n = (1ll << m);
	for(int i = 0; i < n; i++)
		scanf("%lld",&a[i]);
	for(int j = 0; j <= m; j++)
		scanf("%lld",&b[j]);
	for(int j = 0; j < n; j++)
		c[j] = c[j >> 1] + (j & 1);
	for(int i = 0; i < n; i++)
		d[i] = b[c[i]];
	p *= n;
	fwt(a,n,1);fwt(d,n,1);
	for(int i = 0; i < n; i++) {
		a[i] = mul(a[i],fpow(d[i],t,p),p);
	}
	fwt(a,n,-1);
	for(int i = 0; i < n; i++)
		printf("%lld\n",a[i]);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值