JZM 去爬山(FWT ,构造)

题解

JZM 终于打完回来了!为了放松一下,JZM 等人瞒着同学们去爬山。

JZM 在山脚买了份地图,地图共有 N 行,M 列,N×M 个格子,每个格子里的数(记为 a i , j a_{i,j} ai,j)表示这座山的高度, 1 ≤ a i , j ≤ K 1\leq a_{i,j}\leq K 1ai,jK 。JZM 发现在每行开头和每列开头都有数字,具体地,每行开头的数字形成序列 A,每列开头的数字形成序列 B,A、B 满足
A i = a i , 1    &    a i , 2    &    a i , 3    & ⋯    &    a i , M ( 1 ≤ i ≤ N ) B j = a 1 , j    ∣    a 2 , j    ∣    a 3 , j    ∣ ⋯    ∣    a N , j ( 1 ≤ i ≤ M ) A_i=a_{i,1}\;\&\;a_{i,2}\;\&\;a_{i,3}\;\&\cdots\;\&\;a_{i,M}(1\leq i\leq N)\\ B_j=a_{1,j}\;|\;a_{2,j}\;|\;a_{3,j}\;|\cdots\;|\;a_{N,j}(1\leq i\leq M) Ai=ai,1&ai,2&ai,3&&ai,M(1iN)Bj=a1,ja2,ja3,jaN,j(1iM)

JZM 突然好奇想问你一个问题:给定 N , M , K N,M,K N,M,K ,共有多少种 A , B A,B A,B 序列的情况存在?两种状况不同当且仅当 A A A 序列中某个数不同或 B B B 序列中某个数不同。答案对 998244353 取模。

2 ≤ N , M ≤ 1 0 18 , 1 ≤ K ≤ 5 × 1 0 5 2 ≤ N, M ≤ 10^{18}, 1 ≤ K ≤ 5 × 10^5 2N,M1018,1K5×105

Sample Input #1

2 2 2

Sample Output #1

15

Sample Input #2

3 3 5

Sample Output #2

615

Sample Input #3

7 10 20

Sample Output #3

329564051

题解

不难发现,由于每个 A i A_i Ai 限制了一整行的 a i , j a_{i,j} ai,j 二进制中哪些 1 必须有,而 B j B_j Bj 又是对一整列求或,其中每一行的数都会有,因此 ( A 1 ∣ A 2 ∣ A 3 ∣ . . . ∣ A N ) ⊆ ( B 1   &   B 2   &   B 3   & . . . &   B M ) (A_1|A_2|A_3|...|A_N)\sube(B_1\,\&\,B_2\,\&\,B_3\,\&...\&\,B_M) (A1A2A3...AN)(B1&B2&B3&...&BM)

这里的 “ ⊆ \sube ” 即二进制下是否为子集( x ⊆ y    ⇔    x    &    y = x x\sube y\;\Leftrightarrow\; x\;\&\;y=x xyx&y=x),满足这个条件后呢?现在我们要证明这是一种方案 A , B A,B A,B 合法的充要条件

如果满足这个条件,我们尝试把矩阵构造出来,先把第 i i i 行都赋值为 A i A_i Ai 的值,然后去满足 B B B ,我们只需要把每一列选一个数赋为 B j B_j Bj 就行了,那么,只要不是只有一行的极端情况,那么就还有数字可以满足 A A A,但是题目已经给出范围: 2 ≤ N , M 2\leq N,M 2N,M ,因此一定有解。

证明了充要条件后,我么就可以放心计数了。枚举 ( A 1 ∣ A 2 ∣ A 3 ∣ . . . ∣ A N ) (A_1|A_2|A_3|...|A_N) (A1A2A3...AN) ,加上此时 A A A 的方案数 × B B B 的方案数,这个只需要快速沃尔什变换就行了,当然也没必要 O ( K log ⁡ 2 k ) O(K\log^2k) O(Klog2k) 去解决,我们只需要卷一次,然后每个单独元素快速幂后卷回来就行。复杂度 O ( K log ⁡ K ) O(K\log K) O(KlogK)

CODE

#include<set>
#include<map>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 500005
#define DB double
#define LL long long
#define ENDL putchar('\n')
#define lowbit(x) (-(x) & (x))
LL read() {
    
    
	LL f = 1,x = 0;char s = getchar();
	while(s < '0' || s > '9') {
    
    if(s=='-')f = -f;s = getchar();}
	while(s >= '0' && s <= '9') {
    
    x=x*10+(s-'0');s = getchar();}
	return f * x;
}
const int MOD = 998244353;
int n,m,i,j,s,o,k;
void MD(int &x) {
    
    if(x>=MOD)x-=MOD;}
int qkpow(int a,LL b) {
    
    
	b %= (MOD-1);int res = 1;
	while(b > 0) {
    
     
		if(b & 1) res = res *1ll* a % MOD;
		a = a *1ll* a % MOD; b >>= 1;
	}return res;
}
int A[MAXN<<2],B[MAXN<<2];
void DWTOR(int *s,int n) {
    
    
	for(int k = 2;k <= n;k <<= 1) {
    
    
		for(int j = 0;j < n;j += k) {
    
    
			for(int i = j;i < j+(k>>1);i ++) {
    
    
				MD(s[i+(k>>1)] += s[i]);
			}
		}
	}return ;
}
void IDWTOR(int *s,int n) {
    
    
	for(int k = n;k > 1;k >>= 1) {
    
    
		for(int j = 0;j < n;j += k) {
    
    
			for(int i = j;i < j+(k>>1);i ++) {
    
    
				MD(s[i+(k>>1)] += MOD-s[i]);
			}
		}
	}return ;
}
void DWTAND(int *s,int n) {
    
    
	for(int k = 2;k <= n;k <<= 1) {
    
    
		for(int j = 0;j < n;j += k) {
    
    
			for(int i = j;i < j+(k>>1);i ++) {
    
    
				MD(s[i] += s[i+(k>>1)]);
			}
		}
	}return ;
}
void IDWTAND(int *s,int n) {
    
    
	for(int k = n;k > 1;k >>= 1) {
    
    
		for(int j = 0;j < n;j += k) {
    
    
			for(int i = j;i < j+(k>>1);i ++) {
    
    
				MD(s[i] += MOD-s[i+(k>>1)]);
			}
		}
	}return ;
}
int main() {
    
    
	freopen("pair.in","r",stdin);
	freopen("pair.out","w",stdout);
	LL N = read(),M = read();m = read();
	int le = 1;while(le <= m) le <<= 1;
	for(int i = 0;i <= m;i ++) {
    
    
		A[i] = B[i] = 1;
	}
	DWTOR(A,le);
	for(int i = 0;i < le;i ++) A[i] = qkpow(A[i],N);
	IDWTOR(A,le);
	DWTAND(B,le);
	for(int i = 0;i < le;i ++) B[i] = qkpow(B[i],M);
	int ans = 0;
	for(int i = 0;i <= m;i ++) {
    
    
		MD(ans += A[i] *1ll* B[i] % MOD);
	}
	printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43960414/article/details/115208000
FWT