• [HNOI2019] 白兔之舞


    Problem

    有一张顶点数为 ((L+1) imes n) 的有向图,每个节点用二元组 ((u,v)) 来表示((0le ule L,1le vle n)),节点 ((u_1,v_1))((u_2,v_2))(w_{v_1,v_2}) 条不同的边,当且仅当 (u_1<u_2)

    初始时白兔在 ((0,x)),每次沿着一条路跳到下一个节点,它可以在任意时刻停止(也可以在起点停止),或者第一维到达 (L) 也会停止。停止时白兔共跳了 (m) 步,第 (i) 个元素表示经过的第 (i) 条边。

    给定 (k)(y(1le yle n)),对于每个 (t(0le t<k)),求有多少种舞曲(假设其长度为 (m))满足 (mmod k=t),且白兔最后停在了坐标第二维为 (y) 的顶点。答案对 (p)(一个质数)取模。

    (10^8<p<2^{30})(1le nle 3)(1le x,yle n)(0le w_{i,j}<p)(1le kle 65536)(kmid p-1)(1le Lle 10^8)

    Sol

    由题,我们写出来转移矩阵 (A)

    [A=left[ egin{matrix} w_{11}&cdots&w_{n1}\ vdots&ddots&vdots\ w_{1n}&cdots&w_{nn}\ end{matrix} ight] ]

    列向量 (X) 满足 (X_x=1),其余为 (0)

    发现第一维和第二维在某种意义上独立。

    变换 (m) 次得到 (A^mX),取出来第 (y) 项乘上组合数 (Lchoose m) 即为长度为 (m) 的舞曲的种类数。

    假设我们只需要求出 (mmod k=t) 的答案和,不难想到 单位根反演

    [egin{aligned} &sum_{m=0}^L{Lchoose m}A^m[mmod k=t]\ =&sum_{m=0}^L{Lchoose m}A^msum_{j=0}^{k-1}frac{w^{j(m-t)}}k\ =&frac1ksum_{j=0}^{k-1}w^{-jt}sum_{m=0}^L{Lchoose m}w^{jm}A^m end{aligned} ]

    推导中暂时略去乘上的列向量 (X),只要最后补上去即可。注意到后半部分中相当于要求

    [sum_{i=0}^n{nchoose i}A^i ]

    由于 (AI=IA=A),满足 交换律,故可以使用二项式定理,得到

    [frac1ksum_{j=0}^{k-1}w^{-jt}(w^jA+I)^L ]

    (y_j=(w^jA+I)^LX) 的第 (y) 项,要求 (t=j) 时的答案,则上面式子可以看成

    [f(x)=frac1ksum_{j=0}^{k-1}y_jx^j ]

    也就是说我们要依次求出来 (f(w^0),f(w^{-1}),f(w^{-2}),cdots)直接 NTT

    (k otequiv 2^l),所以我们需要 Bluestein's Algorithm 来做任意长度的卷积,详见我的另一篇博文 Chirp Z-Transform

    复杂度 (mathcal O(n^3klog L+klog k))。此题毒瘤还要 MTT

    Code

    #include <bits/stdc++.h>
    using std::vector;
    typedef long long LL;
    const double PI = acos(-1);
    const int N = (1 << 16) + 5;
    int n, k, L, x, y, p, g, w, a[N * 4], b[N * 4], c[N * 4], ans[N];
    struct Mat {
    	int a[3][3], n, m;
    	int *operator [] (int i) { return a[i]; }
    	Mat(int n = 0, int m = 0) : n(n), m(m) {
    		memset(a, 0, sizeof a);
    	}
    } A, X, I;
    Mat operator + (Mat a, Mat b) {
    	for (int i = 0; i < a.n; i++)
    		for (int j = 0; j < a.m; j++)
    			a[i][j] = (a[i][j] + b[i][j]) % p;
    	return a;
    }
    Mat operator * (Mat a, Mat b) {
    	Mat c(a.n, b.m);
    	for (int k = 0; k < a.m; k++)
    		for (int i = 0; i < a.n; i++)
    			for (int j = 0; j < b.m; j++)
    				c[i][j] = (c[i][j] + 1LL * a[i][k] * b[k][j]) % p;
    	return c;
    }
    Mat operator * (int k, Mat a) {
    	for (int i = 0; i < a.n; i++)
    		for (int j = 0; j < a.m; j++)
    			a[i][j] = 1LL * a[i][j] * k % p;
    	return a;
    }
    Mat qpow(Mat a, int b) {
    	Mat c(a.n, a.n);
    	for (int i = 0; i < a.n; i++) c[i][i] = 1;
    	for (; b; b >>= 1, a = a * a)
    		if (b & 1) c = c * a;
    	return c;
    }
    int qpow(int a, int b) {
    	int c = 1;
    	for (; b; b >>= 1, a = 1LL * a * a % p)
    		if (b & 1) c = 1LL * c * a % p;
    	return c;
    }
    struct comp {
    	double x, y;
    	comp(double x = 0, double y = 0) : x(x), y(y) {}
    };
    comp operator + (comp a, comp b) { return {a.x + b.x, a.y + b.y}; }
    comp operator - (comp a, comp b) { return {a.x - b.x, a.y - b.y}; }
    comp operator * (comp a, comp b) { return comp{a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x}; }
    comp conj(comp a) { return {a.x, -a.y}; }
    comp W[N * 4];
    int pw[N * 4], ipw[N * 4];
    void prework(int n) {
    	for (int i = 1; i < n; i <<= 1)
    		for (int j = 0; j < i; j++)
    			W[i + j] = {cos(PI / i * j), sin(PI / i * j)};
    	pw[0] = pw[1] = ipw[0] = ipw[1] = 1, pw[2] = w, ipw[2] = qpow(w, p - 2);
    	for (int i = 3; i < n; i++)
    		pw[i] = 1LL * pw[i - 1] * pw[2] % p, ipw[i] = 1LL * ipw[i - 1] * ipw[2] % p;
    	for (int i = 3; i < n; i++)
    		pw[i] = 1LL * pw[i] * pw[i - 1] % p, ipw[i] = 1LL * ipw[i] * ipw[i - 1] % p;
    }
    void fft(comp *a, int n, int op) {
    	static int rev[N * 4];
    	for (int i = 0; i < n; i++)
    		if ((rev[i] = rev[i >> 1] >> 1 | (i & 1 ? n >> 1 : 0)) > i) std::swap(a[i], a[rev[i]]);
    	for (int q = 1; q < n; q <<= 1)
    		for (int p = 0; p < n; p += q << 1)
    			for (int i = 0; i < q; i++) {
    				comp t = W[q + i] * a[p + q + i];
    				a[p + q + i] = a[p + i] - t; a[p + i] = a[p + i] + t;
    			}
    	if (op) return;
    	for (int i = 0; i < n; i++) a[i].x /= n, a[i].y /= n;
    	std::reverse(a + 1, a + n);
    }
    int getsz(int n) {
    	int x = 1;
    	while (x < n) x <<= 1;
    	return x;
    }
    void conv(int *x, int *y, int *z, int n) {
    	static comp a[N * 4], b[N * 4], da[N * 4], db[N * 4], dc[N * 4], dd[N * 4];
    	for (int i = 0; i < n; i++)
    		a[i] = comp(x[i] >> 15, x[i] & 32767), b[i] = comp(y[i] >> 15, y[i] & 32767);
    	fft(a, n, 1), fft(b, n, 1);
    	for (int i = 0; i < n; i++) {
    		int j = (n - 1) & (n - i);
    		static comp a1, a2, b1, b2;
    		a1 = (a[i] + conj(a[j])) * comp{0.5, 0};
    		a2 = (a[i] - conj(a[j])) * comp{0, -0.5};
    		b1 = (b[i] + conj(b[j])) * comp{0.5, 0};
    		b2 = (b[i] - conj(b[j])) * comp{0, -0.5};
    		da[i] = a1 * b1, db[i] = a1 * b2, dc[i] = a2 * b1, dd[i] = a2 * b2;
    	}
    	for (int i = 0; i < n; i++)
    		a[i] = da[i] + db[i] * comp{0, 1}, b[i] = dc[i] + dd[i] * comp{0, 1};
    	fft(a, n, 0), fft(b, n, 0);
    	for (int i = 0; i < n; i++) {
    		int ax = (LL)(a[i].x + 0.5) % p, ay = (LL)(a[i].y + 0.5) % p, bx = (LL)(b[i].x + 0.5) % p, by = (LL)(b[i].y + 0.5) % p;
    		z[i] = (((LL)ax << 30) + ((LL)(ay + bx) << 15) + by) % p;
    	}
    }
    bool check(int n) {
    	for (int i = 2; i * i <= n; i++)
    		if (!(n % i)) return 0;
    	return 1;
    }
    void getG() {
    	vector<int> fac;
    	int tmp = p - 1;
    	for (int i = 2; !check(tmp); i++)
    		while (tmp % i == 0) fac.push_back(i), tmp /= i;
    	if (tmp != 1) fac.push_back(tmp);
    	for (;; g++) {
    		int ok = 1;
    		for (int i = 0; i < fac.size(); i++)
    			if (qpow(g, (p - 1) / fac[i]) == 1) { ok = 0; break; }
    		if (ok) break;
    	}
    }
    int main() {
    	scanf("%d%d%d%d%d%d", &n, &k, &L, &x, &y, &p);
    	A = Mat(n, n);
    	for (int i = 0; i < n; i++)
    		for (int j = 0; j < n; j++)
    			scanf("%d", &A[j][i]);
    	X = Mat(n, 1), X[x - 1][0] = 1;
    	I = Mat(n, n);
    	for (int i = 0; i < n; i++) I[i][i] = 1;
    	g = 1; getG();
    	w = qpow(g, (p - 1) / k);
    	int l = getsz(k * 2); prework(l);
    	for (int i = 0; i < k; i++)
    		a[i] = 1LL * (qpow(qpow(w, i) * A + I, L) * X)[y - 1][0] * ipw[i] % p;
    	std::reverse(a, a + k + 1);
    	for (int i = 0; i < k * 2; i++) b[i] = pw[i];
    	conv(a, b, c, l);
    	for (int i = 0; i < k; i++)
    		ans[i ? k - i : 0] = 1LL * c[k + i] * ipw[i] % p * qpow(k, p - 2) % p;
    	for (int i = 0; i < k; i++)
    		printf("%d
    ", ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    JavaSE 基础 第51节 定义自己的异常
    JavaSE 基础 第50节 Java中的异常链
    JavaSE 基础 第49节 手动抛出异常
    JavaSE 基础 第48节 Java中的异常声明
    JavaSE 基础 第47节 获取异常信息
    JavaSE 基础 第46节 异常的分类
    JavaSE 基础 第45节Java异常快速入门
    JavaSE 基础 第44节 引用外部类的对象
    JavaSE 基础 第43节 静态内部类
    通用爬虫
  • 原文地址:https://www.cnblogs.com/ac-evil/p/14759349.html
Copyright © 2020-2023  润新知