題目背景
Wwq 為了能破解室友的 QQ 密碼特地選修了信科密碼學的課。然后,他終於在開學一個月后意識到這個課好像和他想象的完全不一樣。
今天老師上課介紹了校驗碼。校驗碼的作用之一是檢查數據在傳輸過程中是否出錯,其歷史可以追溯到猶太人寫聖經。
聰明的希伯來人在謄寫聖經的時候為了保證絕對正確,除了提醒自己要仔細和反復檢查外,他們還采取了一種特殊的方法——他們給希伯來字母編了碼,這樣聖經的每一行都是一串數字,他們把這串數字經過一定的運算后得到一個數字,再轉化回希伯來字母。
這樣聖經的單獨每一行都有一個校驗字母,每一列也有一個校驗字母,他們只要在謄寫完一頁聖經后立刻對比所有的校驗字母是否和原聖經相同即可。(我們一般認為在同一行犯錯兩次的概率比較低)
題目描述
Wwq 被這種簡單而有效的數學美打動了,他決定也用這種方法來生成他昨晚做題時得到的一個矩陣的校驗碼。(雖然我們根本不知道 Wwq 生成校驗碼能做什么,也許是單純為了好玩)
為了處理 Wwq 最頭疼的溢出問題,Wwq 在計算校驗碼時直接使用了位運算 \(|(or)\),這樣他處理任何數都不用考慮溢出的問題了。
也就是說,Wwq 現在有一個 \(N \times M\) 的矩陣,矩陣中每個數都是一個 K 位 2 進制數,Wwq 會把矩陣每一行的數都或起來,得到 N 個數,他也會把每一列的數都或起來,得到 M 個數,這樣他就有了 \(N + M\)個校驗碼。
然后——Wwq 發現他的矩陣在計算后得到的 \(N+M\) 個校驗碼全是 \(2^k - 1\) ……
Wwq 現在有了個新問題,有多少種不同的情況會導致得到的校驗碼全是 \(2^k-1\) 呢?
即,有多少個 \(N \times M\) 的矩陣滿足:其所有數字都是 K 位 2 進制數,且每一行每一列的或都是 \(2^k - 1\)。
輸入格式
多組測試數據,在輸入的第一行會給出測試數據組數 T 。
每組測試數據一共一行,為三個整數 N,M,K 。
輸出格式
輸出每組數據一行,即答案。
樣例輸入
4
1 1 31
1 10 2
2 2 1
2 3 2
樣例輸出
1
1
7
625
提示&說明
對於 \(30\%\) 的數據,N,M 不超過 10。
對於 \(100\%\) 的數據, N,M 不超過 50 , K 不超過 31。
題解
因為二進制下的位與位之間沒有關系,因此我們可以先考慮 k = 1 時的情況。
我們把行和列分開來考慮, 看每一行,答案的情況就有 \(2^n\) 種, 因為有一種都是零的情況不合題意,因此每一行的答案就是, \(2^n - 1\),又因為有 m 行, 所以答案就是 \((2^n-1 )^m\) , 然后你感覺這個東西有點不對,好像算重了一部分,然后你會發現還要乘上一個容斥系數。
然后就是 \(\displaystyle ans = \sum_{i = 0} ^n (-1)^i{n \choose i} (2^i-1)^m\)
這個是 k=1 的時候的答案,最后的時候 \(ans ^k\)就行了.
code
#include <bits/stdc++.h>
#define ll long long
#define N 100010
#define M 60
using namespace std;
const int mod = 1e9+7;
ll n, m, k, C[M][M];
ll read() {
ll s = 0, f = 0;
char ch = getchar();
while (!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
return f ? -s : s;
}
ll q_pow(ll a, ll b) {
ll ans = 1;
while (b) {
if (b & 1) ans = (ans * a) % mod;
a = (a * a) % mod;
b >>= 1;
}
return ans;
}
ll F(ll x) {
return q_pow((q_pow(2,x) - 1),m);
}
void get_c() {
for (int i = 0; i <= 50; i++) C[i][0] = 1;
for (int i = 1; i <= 50; i++)
for (int j = 1; j <= i; j++)
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
}
int main() {
freopen("code.in", "r", stdin);
freopen("code.out", "w", stdout);
get_c();
int T = read();
while (T--) {
n = read(), m = read(), k = read();
if (n > m) swap(n, m);
ll ans = 0;
for (ll i = 0; i <= n; i++) {
ll b = 1;
if (i & 1) b = -1;
ans = (ans + (C[n][i] * F(n - i) * b % mod + mod) % mod) % mod;
}
printf("%lld\n", q_pow(ans, k));
}
return 0;
}