UOJ#272. 【清华集训2016】石家庄的工人阶级队伍比较坚强

链接:

link

题解:

直接讲构造FWT吧,其他都很简单不想说了。
这里是做 3 进制异或,考虑分治乘。
T0=A0B0+A1B2+A2B1
T1=A0B1+A1B0+A2B2
T2=A0B2+A1B1+A2B0
如果使用待定系数法,用 (A0+xA1+yA2)(B0+xB1+yB2) 来构造,那么发现需要满足三个条件:
xy=1,x2=y,y2=x
这提示我们三次单位根。记 w 表示三次单位根,有:
Ci=(A0+wiA1+w2iA2)(B0+wiB1+w2iB2),考虑用 C 构造出T
注意到 w2+w+1=0
那么 (1111ww21w2w)×(1111w2w1ww2)=3I ,其中 I 是单位矩阵。
还有一个问题是w的处理,我们将数表示为 a+bw ,利用 w2=w1 就可以进行计算了。
这里只需要 3 在模p意义下有逆元即可,比题目的限制更松了。

代码:

#include <bits/stdc++.h>
#define xx first
#define yy second
#define mp make_pair
#define pb push_back
#define mset(x, y) memset(x, y, sizeof x)
#define mcpy(x, y) memcpy(x, y, sizeof x)
using namespace std;

typedef long long LL;
typedef pair <int, int> pii;

inline int Read()
{
    int x = 0, f = 1, c = getchar();
    for (; !isdigit(c); c = getchar())
        if (c == '-')
            f = -1;
    for (;  isdigit(c); c = getchar())
        x = x * 10 + c - '0';
    return x * f;
}

const int MAXN = 15;
const int MAXM = 531445;

int n, m, t, mod, pwd[MAXN], cnx[MAXM], cny[MAXM], trans[MAXN][MAXN];

struct Number
{
    int x, y;
    Number(int _x = 0, int _y = 0) { x = _x, y = _y; }
    Number operator + (const Number &b) const { return Number((x + b.x) % mod, (y + b.y) % mod); }
    Number operator - (const Number &b) const { return Number((x - b.x + mod) % mod, (y - b.y + mod) % mod); }
    Number operator * (const Number &b) const { return Number((1LL * x * b.x - 1LL * y * b.y % mod + mod) % mod, (1LL * x * b.y + 1LL * y * b.x - 1LL * y * b.y % mod + mod) % mod); }
} a[MAXM], b[MAXM];

Number Qow(Number x, int y)
{
    Number r(1, 0);
    for (; y; y >>= 1, x = x * x)
        if (y & 1)
            r = r * x;
    return r;
}

inline void Exgcd(int a, int b, int &x, int &y)
{
    if (!b)
        x = 1, y = 0;
    else
        Exgcd(b, a % b, y, x), y -= a / b * x;
}

inline void DFT(Number *a)
{
    Number b[3];
    b[0] = a[0] + a[1] + a[2];
    b[1] = Number((1LL * a[0].x - a[1].y - a[2].x + a[2].y + (mod << 1)) % mod, (1LL * a[0].y + a[1].x - a[1].y - a[2].x + (mod << 1)) % mod);
    b[2] = Number((1LL * a[0].x - a[1].x + a[1].y - a[2].y + (mod << 1)) % mod, (1LL * a[0].y - a[1].x + a[2].x - a[2].y + (mod << 1)) % mod);
    a[0] = b[0], a[1] = b[1], a[2] = b[2];
}

inline void IDFT(Number *a)
{
    Number b[3];
    b[0] = a[0] + a[1] + a[2];
    b[2] = Number((1LL * a[0].x - a[1].y - a[2].x + a[2].y + (mod << 1)) % mod, (1LL * a[0].y + a[1].x - a[1].y - a[2].x + (mod << 1)) % mod);
    b[1] = Number((1LL * a[0].x - a[1].x + a[1].y - a[2].y + (mod << 1)) % mod, (1LL * a[0].y - a[1].x + a[2].x - a[2].y + (mod << 1)) % mod);
    a[0] = b[0], a[1] = b[1], a[2] = b[2];
}

inline void FWT(Number *x, int typ)
{
    for (int i = 0; i < m; i ++)
        for (int j = 0; j < n; j ++)
            if (j / pwd[i] % 3 == 0)
            {
                Number t[3] = {x[j], x[j + pwd[i]], x[j + (pwd[i] << 1)]};
                if (typ)
                    DFT(t);
                else
                    IDFT(t);
                x[j] = t[0], x[j + pwd[i]] = t[1], x[j + (pwd[i] << 1)] = t[2];
            }
}

int main()
{
#ifdef wxh010910
    freopen("data.in", "r", stdin);
#endif
    m = Read(), t = Read(), mod = Read();
    pwd[0] = 1;
    for (int i = 1; i <= m; i ++)
        pwd[i] = pwd[i - 1] * 3;
    n = pwd[m];
    for (int i = 0; i < n; i ++)
        a[i].x = Read();
    for (int i = 0; i <= m; i ++)
        for (int j = 0; i + j <= m; j ++)
            trans[i][j] = Read();
    for (int i = 0; i < n; i ++)
        cnx[i] = cnx[i / 3] + (i % 3 == 1), cny[i] = cny[i / 3] + (i % 3 == 2);
    for (int i = 0; i < n; i ++)
        b[i].x = trans[cnx[i]][cny[i]];
    FWT(a, 0), FWT(b, 0);
    for (int i = 0; i < n; i ++)
        a[i] = a[i] * Qow(b[i], t);
    FWT(a, 1);
    int inv, tmp;
    Exgcd(n, mod, inv, tmp);
    inv = (inv + mod) % mod;
    for (int i = 0; i < n; i ++)
        printf("%d\n", 1LL * a[i].x * inv % mod);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值