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

传送门

设运算 \(op1,op2\),一个表示三进制不进位的加法,一个表示不退位的减法
设 \(cnt1[x],cnt2[x]\) 分别表示 \(x\) 转成三进制后 \(1/2\) 的个数
那么
\(f_{i,x}=\sum f_{i-1,y}b_{cnt1[x~op2~y],cnt2[x~op2~y]}\)
设 \(B_{x,y}=b_{cnt1[x~op2~y],cnt2[x~op2~y]}\)
那么可以发现 \(B_{x,y}=B_{x~op2~y,0}\)
那么我们要求的就是 \(f\) 与 \(B\) 的第一行的 \(t\) 次卷积的卷积
其中下标运算为 \(op1\)
那么我们求出 \(f\) 和 \(B\) 的"点值表达",快速幂之后变换回去即可
下标运算可以看成是每一位的模 \(3\) 的循环卷积,用三次单位根 \(FWT\),每一层手动做一遍长度为 \(3\) 的 \(DFT\)
由于题目中 \(p\) 的性质,可以得到 \(3\perp p\),所以 \(3\) 有逆元
注意到 \(\omega_3^2+\omega_3+1=0\)
把所有数字用 \(a+b\omega\) 表示,重定义运算即可

# include <bits/stdc++.h>
using namespace std;
typedef long long ll;

namespace IO {
    const int maxn(1 << 21 | 1);

    char ibuf[maxn], obuf[maxn], *iS, *iT, *oS = obuf, *oT = obuf + maxn - 1, c, st[66];
    int tp, f;

    inline char Getc() {
        return iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, maxn, stdin), (iS == iT ? EOF : *iS++)) : *iS++;
    }

    template <class Int> inline void In(Int &x) {
        for (f = 1, c = Getc(); c < '0' || c > '9'; c = Getc()) f = c == '-' ? -1 : 1;
        for (x = 0; c >= '0' && c <= '9'; c = Getc()) x = (x << 1) + (x << 3) + (c ^ 48);
        x *= f;
    }

    inline void Flush() {
        fwrite(obuf, 1, oS - obuf, stdout);
        oS = obuf;
    }

    inline void Putc(char c) {
        *oS++ = c;
        if (oS == oT) Flush();
    }

    template <class Int> void Out(Int x) {
        if (!x) Putc('0');
        if (x < 0) Putc('-'), x = -x;
        while (x) st[++tp] = x % 10 + '0', x /= 10;
        while (tp) Putc(st[tp--]);
    }
}

using IO :: In;
using IO :: Out;
using IO :: Putc;
using IO :: Flush;

const int maxn(531441);

int mod, m, t, n, bin[20], b[20][20], cnt1[maxn], cnt2[maxn], phi, inv3;

inline int Pow(ll x, int y) {
    ll ret = 1;
    for (; y; y >>= 1, x = x * x % mod)
        if (y & 1) ret = ret * x % mod;
    return ret;
}

inline void Inc(int &x, int y) {
    x = x + y >= mod ? x + y - mod : x + y;
}

inline void Dec(int &x, int y) {
    x = x - y < 0 ? x - y + mod : x - y;
}

inline int Add(int x, int y) {
    return x + y >= mod ? x + y - mod : x + y;
}

inline int Sub(int x, int y) {
    return x - y < 0 ? x - y + mod : x - y;
}

struct Complex {
    int a, b;

    inline Complex(int _a = 0, int _b = 0) {
        a = _a, b = _b;
    }

    inline Complex W1() {
        return Complex(Sub(0, b), Sub(a, b));
    }

    inline Complex W2() {
        return Complex(Sub(b, a), Sub(0, a));
    }

    inline Complex operator +(Complex y) const {
        return Complex(Add(a, y.a), Add(b, y.b));
    }

    inline Complex operator -(Complex y) const {
        return Complex(Sub(a, y.a), Sub(b, y.b));
    }

    inline Complex operator *(Complex y) const {
        return Complex(Sub((ll)a * y.a % mod, (ll)b * y.b % mod), Sub(Add((ll)a * y.b % mod, (ll)b * y.a % mod), (ll)b * y.b % mod));
    }

    inline Complex operator *(int y) const {
        return Complex((ll)a * y % mod, (ll)b * y % mod);
    }
} coef[maxn], f[maxn], tmp[3];

inline Complex PowComplex(Complex x, int y) {
    Complex ret = Complex(1, 0);
    for (; y; y >>= 1, x = x * x) if (y & 1) ret = ret * x;
    return ret;
}

inline void DFWT(Complex *p, int opt) {
    int i, j, k, t;
    for (i = 1; i < n; i *= 3)
        for (j = 0, t = i * 3; j < n; j += t)
            for (k = 0; k < i; ++k) {
                tmp[0] = p[j + k], tmp[1] = p[j + k + i], tmp[2] = p[j + k + i + i];
                p[j + k] = tmp[0] + tmp[1] + tmp[2];
                p[j + k + i] = tmp[0] + tmp[1].W1() + tmp[2].W2();
                p[j + k + i + i] = tmp[0] + tmp[1].W2() + tmp[2].W1();
                if (opt == -1) {
                    swap(p[j + k + i], p[j + k + i + i]);
                    p[j + k] = p[j + k] * inv3;
                    p[j + k + i] = p[j + k + i] * inv3;
                    p[j + k + i + i] = p[j + k + i + i] * inv3;
                }
            }
}

int main() {
    freopen("b.in", "r", stdin);
    int i, j, x;
    In(m), In(t), In(mod);
    for (i = bin[0] = 1; i < 20; ++i) bin[i] = bin[i - 1] * 3;
    n = bin[m];
    if (mod == 1) {
        for (i = 0; i < n; ++i) Putc('0'), Putc('\n');
        return Flush(), 0;
    }
    x = phi = mod;
    for (i = 2; i * i <= x; ++i)
        if (x % i == 0) {
            phi -= phi / i;
            while (x % i == 0) x /= i;
        }
    if (x > 1) phi -= phi / x;
    inv3 = Pow(3, phi - 1);
    for (i = 0; i < n; ++i) {
        cnt1[i] = cnt1[i / 3] + (i % 3 == 1);
        cnt2[i] = cnt2[i / 3] + (i % 3 == 2);
    }
    for (i = 0; i < n; ++i) In(f[i].a);
    for (i = 0; i <= m; ++i)
        for (j = 0; j <= m - i; ++j) In(b[i][j]);
    for (i = 0; i < n; ++i) coef[i].a = b[cnt1[i]][cnt2[i]];
    DFWT(coef, 1), DFWT(f, 1);
    for (i = 0; i < n; ++i) f[i] = f[i] * PowComplex(coef[i], t);
    DFWT(f, -1);
    for (i = 0; i < n; ++i) Out(f[i].a), Putc('\n');
    return Flush(), 0;
}

原文地址:https://www.cnblogs.com/cjoieryl/p/10327352.html

时间: 2024-10-07 11:07:14

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

codeforces 1103E\清华集训 石家庄的工人阶级队伍比较坚强

希望复习高进制FWT的时候,能够快速回想起来. FWT感觉就是每一维单独考虑,(虽然我不知道为什么这是对的) 分别对一个奇怪的东西做DFT, 那个奇怪的东西在k进制下就是关于k次单位根的范德蒙特矩阵. 范德蒙特矩阵的逆矩阵大致就是每行除了第一个数之外翻转一下,然后除以矩阵的阶. 也可理解为原矩阵把k次单位根取反,反正每一维上就是个DFT,按FFT的做法搞就行了. 然后这两道题毒瘤的地方就在于不能用实数来表示一个复数,否则会爆精. 而且你也不一定可以算出单位根的值,十分毒瘤. 于是你就需要搞事,

UOJ272. 【清华集训2016】石家庄的工人阶级队伍比较坚强 [FWT]

UOJ 思路 很容易想到\(O(3^{3m}\log T)\)的暴力大矩乘,显然过不了. 我们分析一下每次转移的性质.题目给的转移方程是填表法,我们试着改成刷表法看看-- 发现好像没啥用. 注意到游戏的规则是1吃0,2吃1,0吃2,也就是在\(x-y=1\pmod 3\)的时候\(x\)吃\(y\). 我们枚举\(j\),然后再枚举\(i\ominus j\)(这里减法是每一位不退位减法),根据\(i\ominus j\)的状态来更新\(f_i\). 换句话说,枚举\(j,k\),然后用\(f_

【UOJ274】【清华集训2016】温暖会指引我们前行 LCT

[UOJ274][清华集训2016]温暖会指引我们前行 任务描述 虽然小R住的宿舍楼早已来了暖气,但是由于某些原因,宿舍楼中的某些窗户仍然开着(例如厕所的窗户),这就使得宿舍楼中有一些路上的温度还是很低. 小R的宿舍楼中有n个地点和一些路,一条路连接了两个地点,小R可以通过这条路从其中任意一个地点到达另外一个地点.但在刚开始,小R还不熟悉宿舍楼中的任何一条路,所以他会慢慢地发现这些路,他在发现一条路时还会知道这条路的温度和长度.每条路的温度都是互不相同的. 小R需要在宿舍楼中活动,每次他都需要从

bzoj 4736 /uoj274【清华集训2016】温暖会指引我们前行 lct

[清华集训2016]温暖会指引我们前行 统计 描述 提交 自定义测试 寒冬又一次肆虐了北国大地 无情的北风穿透了人们御寒的衣物 可怜虫们在冬夜中发出无助的哀嚎 “冻死宝宝了!” 这时 远处的天边出现了一位火焰之神 “我将赐予你们温暖和希望!” 只见他的身体中喷射出火焰之力 通过坚固的钢铁,传遍了千家万户 这时,只听见人们欢呼 “暖气来啦!” 任务描述 虽然小R住的宿舍楼早已来了暖气,但是由于某些原因,宿舍楼中的某些窗户仍然开着(例如厕所的窗户),这就使得宿舍楼中有一些路上的温度还是很低. 小R的

【UOJ】#273. 【清华集训2016】你的生命已如风中残烛

题目链接:http://uoj.ac/problem/273 $${Ans=\frac{\prod _{i=1}^{m}i}{w-n+1}}$$ 1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<vector> 5 #include<cstdlib> 6 #include<cmath> 7 #include<cstring> 8

UOJ 274 【清华集训2016】温暖会指引我们前行 ——Link-Cut Tree

魔法森林高清重置, 只需要维护关于t的最大生成树,然后链上边权求和即可. 直接上LCT 调了将近2h 吃枣药丸 #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define F(i,j,k) for (int i=j;i<=k;++i) #define D(i,j,k) for (int i=j;i&g

uoj#268. 【清华集训2016】数据交互(动态dp+堆)

传送门 动态dp我好像还真没咋做过--通过一个上午的努力光荣的获得了所有AC的人里面的倒数rk3 首先有一个我一点也不觉得显然的定理,如果两条路径相交,那么一定有一条路径的\(LCA\)在另一条路径上 于是我们可以对于每一个点记录两个值,一个\(a_i\)表示\(LCA\)在\(i\)点的所有路径的权值之和,一个是\(b_i\),表示经过点\(i\)且\(LCA\)不在点\(i\)的所有路径的权值之和 那么对于一条路径\((u,v)\),它的权值就是\(b_{LCA(u,v)}+\sum_{i\

uoj#269. 【清华集训2016】如何优雅地求和(数论)

传送门 首先,如果\(f(x)=1\),那么根据二项式定理,有\(Q(f,n,k)=1\) 当\(f(x)=x\)的时候,有\[Q=\sum_{i=0}^ni\times \frac{n!}{i!(n-i)!}k^i(1-k)^{n-i}\] \[Q=\sum_{i=0}^nnk\times \frac{(n-1)!}{(i-1)!(n-i)!}k^{i-1}(1-k)^{n-i}\] \[Q=nk\sum_{i=0}^n\frac{(n-1)!}{(i-1)!(n-i)!}k^{i-1}(1-

uoj#273. 【清华集训2016】你的生命已如风中残烛(组合数学)

传送门 一道打表题 我们把那些普通牌的位置看成\(-1\),那么就是要求有多少个排列满足前缀和大于等于\(1\) 考虑在最后放一个\(-1\),那么就是除了\(m+1\)的位置前缀和都要大于等于\(1\) \(m+1\)个数的圆排列的方案数为\(m!\),然后对于每一个圆排列,肯定存在一个前缀和最小且最右边的位置,那么它后面的所有位置肯定前缀和都大于等于\(1\),而对于这个位置如果不把它放最后肯定会有前缀和小于\(1\) 所以对于每一种圆排列有且仅有一种摆放方式合法 然而此时最后的这个\(-1