Description

小春现在很清闲, 面对书桌上的 \(n\) 张牌, 他决定给每张染色, 目前小春只有 3 种颜色: 红色, 蓝色, 绿色. 他询问 Sun 有多少种染色方案, Sun 很快就给出了答案. 进一步, 小春要求 染出 \(S_r\) 张红色,\(S_b\) 张蓝色,\(S_g\) 张绿色. 他又询问有多少种方案, Sun 想了一下, 又给出了正确答案. 最后小春发明了 \(m\) 种不同的洗牌法, 这里他又问 Sun 有多少种不同 的染色方案.

两种染色方法相同当且仅当其中一种可以通过任意的洗牌法 (即可以使用多种洗牌法, 而每种方法可以使用多次) 洗成另一种. Sun 发现这个问题有点难度, 决定交给你, 答案可能 很大, 只要求出答案除以 \(P\) 的余数 (\(P\) 为质数).

Input

第一行输入 5 个整数:\(S_r, S_b, S_g, m, p (m \leq 60, m+1 \lt p \lt 100), n = S_r + S_b + S_g\).

接下来 \(m\) 行, 每行描述一种洗牌法, 每行有 \(n\) 个用空格隔开的整数 \(X_1, X_2, \ldots, X_n\), 恰为 \(1\)\(n\) 的一个排列, 表示使用这种洗牌法, 第 \(i\) 位变为原来的 \(X_i\) 位的牌. 输入数据保证任意多次洗牌都可用这 \(m\) 种洗牌法中的一种代替, 且对每种洗牌法, 都存在一种洗牌法使得能回到原状态.

Output

输出一个整数, 表示不同染法除以 \(P\) 的余数.

Sample Input

1 1 1 2 7
2 3 1
3 1 2

Sample Output

2

Data Range

100% 数据满足 \(max(S_r, S_b, S_g) \leq 20\).

Explanation

Burnside 定理: 有 m 个置换 k 种颜色, 所有本质不同的染色方案数就是每种置换的不变元素的 个数的平均数. 所谓不变元素就是一种染色方案经过置换变换后和没变化之前一样. 所以 现在就是需要求出不变元素, 同时还要满足各种颜色数量的限制. 置换的循环在不变元素中一定是一个颜色, 然后可以求一个三维的 01 背包的方案数. 而最后的除法需要利用扩展 欧几里得求乘法的逆元.

Source Code


#include <iostream>
#include <cstdlib>
#include <cstdio>

using namespace std;
typedef long long lli;
const int maxm = 100, maxn = 100;

int modr = 0;
int s1, s2, s3, n, m, p;
int arr[maxm][maxn];

int dp[maxm][maxn][maxn];
int d[maxn];
bool vis[maxn];
int eval(int x)
{
    for (int i = 1; i <= n; i++)
        vis[i] = false;
    int sum = 0, p;
    // Iterate through colours 1 ~ n
    for (int i = 1; i <= n; i++) if (!vis[i]) {
        // Circulate around the scheme
        d[++sum] = 1; p = i;
        vis[p] = true;
        while (!vis[arr[x][p]]) {
            d[sum] += 1;
            vis[arr[x][p]] = 1;
            p = arr[x][p];
        }
    }
    // Clean dp setup
    for (int i = s1; i >= 0; i--)
        for (int j = s2; j >= 0; j--)
            for (int k = s3; k >= 0; k--)
                dp[i][j][k] = 0;
    dp[0][0][0] = 1;
    // Dynamic programming
    #define modr_add(__x,__y) __x=(__x+__y)%modr
    for (int h = 1; h <= sum; h++)
        for (int i = s1; i >= 0; i--)
            for (int j = s2; j >= 0; j--)
                for (int k = s3; k >= 0; k--) {
                    if (i >= d[h])
                        modr_add(dp[i][j][k], dp[i - d[h]][j][k]);
                    if (j >= d[h])
                        modr_add(dp[i][j][k], dp[i][j - d[h]][k]);
                    if (k >= d[h])
                        modr_add(dp[i][j][k], dp[i][j][k - d[h]]);
                }
    // Returning results
    return dp[s1][s2][s3];
}

void ext_gcd(int a, int b, int &x, int &y)
{
    if (b == 0) {
        x = 1, y = 0;
        return ;
    }
    ext_gcd(b, a % b, x, y);
    int t = x; x = y;
    y = t - a / b * y;
    return ;
}

int main(int argc, char** argv)
{
    scanf("%d%d%d%d%d", &s1, &s2, &s3, &m, &p);
    n = s1 + s2 + s3;
    for (int i = 1; i <= m; i++)
        for (int j = 1; j <= n; j++)
            scanf("%d", &arr[i][j]);
    modr = p; // Modulo
    // This points to itself (retains data)
    m += 1;
    for (int j = 1; j <= n; j++)
        arr[m][j] = j;
    // Retrieving results
    int res = 0;
    for (int i = 1; i <= m; i++)
        res = (res + eval(i)) % modr;
    // modr-base division modulo
    int x, y;
    ext_gcd(m, modr, x, y);
    while (x <= 0)
        x += modr, y -= m;
    printf("%d\n", res * x % modr);
    return 0;
}