• [luogu 3923]. 大学数学题


    题意

    构造一个(n)元有限域,若不存在输出-1,否则域中元素分别用(0, 1, 2, ldots, n - 1)表示,输出加法表和乘法表。
    (n leq 343)

    题解

    我真的会域扩张吗?答:不会
    首先,根据一些基础知识,有解当且仅当(n = p ^ d),其中(p in mathbb{P})
    其次,根据域扩张的知识,我们要构造的( ext{GF}(p ^ d))一定是由素域( ext{GF}(p))有限扩张而来的。
    考察在( ext{GF}(p))下的代数扩张,即假设存在一个( ext{GF}(p)[[x]])下的不可约多项式(f(x)),设(zeta)是这个多项式的一个根,那么( ext{GF}(p ^ d))中的所有元素可以唯一表示成一个多项式

    [a_0 + a_1 zeta + a_2 {zeta} ^ 2 + ldots + a_{d - 1} {zeta} ^ {d - 1} (a_i in ext{GF}(p)) ]

    (实际上是基(1, zeta, {zeta} ^ 2, ldots, {zeta} ^ {d - 1})的线性组合)
    假设我们最后要输出的是这些多项式的加法表和乘法表,那么问题就转化为了找到一个合适的加法运算和乘法运算。
    加法就是在域( ext{GF}(p)[[x]])下的多项式加法,乘法就是在域( ext{GF}(p)[[x]])下的多项式乘法。
    但是多项式乘法会导致使用的基的大小变大,我们需要把基的大小缩到(d)
    考虑可以应用线性组合(f(zeta) = 0)
    对于一个多项式,加上或减去(k)倍的(f(zeta))(k in ext{GF}(p)[[x]])),代入(zeta)求值不变。
    那么对于一个多项式,如果次数超过了(d - 1),可以将它对(f(zeta))取模,得到的结果是一个次数小于等于(d - 1)的多项式(即基的大小缩到了(d)),并且代入(zeta)后值和原多项式值相等。
    现在考虑的是我们输出的是一个数表而不是多项式表,考虑到要把多项式(F)映射到数(f),并且要求这个映射可逆。
    如果有办法可以把(zeta)搞出来,那么当然可以直接将(F)映射到(F(zeta))上,这是个同构映射,显然可逆。
    但是求(zeta)是不现实的,我们退而求其次,找一个要求不太高的可逆映射即可,比如把多项式(F)的系数看做数(f)(p)进制下每一位的值,可以验证这是可行的。
    复杂度(mathcal O(n ^ 2 {log} ^ 2 n))

    #include <bits/stdc++.h>
    using namespace std;
    typedef vector <int> poly;
    const int N = 360, M = 10;
    const int R[7][8] = {
    {1, 7, 11, 19, 37, 67, 131, 283}, // 2, 4, 8, 16, 32, 64, 128, 256
    {1, 10, 34, 86, 250, -1, -1, -1}, // 3, 9, 27, 81, 243
    {1, 27, 131, -1, -1, -1, -1, -1}, // 5, 25, 125
    {1, 50, 345, -1, -1, -1, -1, -1}, // 7, 49, 343
    {1, 122, -1, -1, -1, -1, -1, -1}, // 11, 121
    {1, 171, -1, -1, -1, -1, -1, -1}, // 13, 169
    {1, 292, -1, -1, -1, -1, -1, -1}, // 17, 289
    };
    int n, id[N], vis[N]; vector <int> pr;
    void sieve () {
    	for (int i = 2; i < N; ++i) {
    		if (!vis[i]) {
    			id[i] = pr.size(), pr.push_back(i);
    			for (int j = i; j < N; j += i) {
    				vis[j] = i;
    			}
    		}
    	}
    }
    namespace GF {
    	int m, p, d, pl[M];
    	struct elem {
    		int x;
    		elem (int _x = 0) : x(_x) {}
    		elem compress (const poly &r) {
    			x = 0;
    			for (int i = d - 1; ~i; --i) {
    				x = x * p + r[i];
    			}
    			return *this;
    		}
    		poly expand (elem e) {
    			poly r(d);
    			for (int i = 0; i < d; ++i) {
    				r[i] = e.x % p, e.x /= p;
    			}
    			return r;
    		}
    		elem operator + (const elem &o) {
    			poly F = expand(*this), G = expand(o);
    			for (int i = 0; i < d; ++i) {
    				F[i] = (F[i] + G[i]) % p;
    			}
    			return compress(F);
    		}
    		elem operator * (const elem &o) {
    			poly F = expand(*this), G = expand(o), H(d + d, 0);
    			for (int i = 0; i < d; ++i) {
    				for (int j = 0; j < d; ++j) {
    					H[i + j] = (H[i + j] + F[i] * G[j]) % p;
    				}
    			}
    			for (int i = (d - 1) << 1; i > d - 1; --i) {
    				H[i] = -H[i] % p, H[i] += H[i] >> 31 & p;
    				for (int j = 1; j <= d; ++j) {
    					H[i - j] = (H[i - j] + H[i] * pl[d - j]) % p;
    				}
    			}
    			return compress(H);
    		}
    	} ;
    	void get_poly () {
    		for (int i = 0, x = R[id[p]][d - 1]; i <= d; ++i) {
    			pl[i] = x % p, x /= p;
    		}
    	}
    } using namespace GF;
    int main () {
    	sieve();
    	cin >> n, m = n, p = vis[n];
    	for (int x = n; x != 1; x /= p, ++d) {
    		if (x % p != 0) {
    			n = 1;
    			break;
    		}
    	}
    	if (n == 1) {
    		puts("-1");
    	} else {
    		puts("0");
    		get_poly();
    		for (int i = 0; i < m; ++i) {
    			for (int j = 0; j < m; ++j) {
    				printf("%d%c", ((elem)i + (elem)j).x, j < n - 1 ? ' ' : '
    ');
    			}
    		}
    		for (int i = 0; i < n; ++i) {
    			for (int j = 0; j < n; ++j) {
    				printf("%d%c", ((elem)i * (elem)j).x, j < n - 1 ? ' ' : '
    ');
    			}
    		}
    	}
    	return 0;
    }
    /*
    4  : x ^ 2 + x     + 1             (111)2       = 7
    8  : x ^ 3 + x     + 1             (1011)2      = 11
    9  : x ^ 2 + 1                     (101)3       = 10
    25 : x ^ 2 + 2                     (102)5       = 27
    27 : x ^ 3 + 2x    + 1             (1021)3      = 34
    81 : x ^ 4 + x     + 2             (10012)3     = 86
    121: x ^ 2 + 1                     (101)11      = 122
    125: x ^ 3 + x     + 1             (1011)5      = 131
    169: x ^ 2 + 2                     (102)13      = 171
    128: x ^ 7 + x     + 1             (10000011)2  = 131
    243: x ^ 5 + 2x    + 1             (1000021)3   = 250
    256: x ^ 8 + x ^ 4 + x ^ 3 + x + 1 (100011011)2 = 283
    343: x ^ 3 + 2                     (1002)7      = 345
    */
    
  • 相关阅读:
    使用Spring AOP实现业务依赖解耦
    对Java提供的锁机制的一些思考
    关于数据库优化的一些想法
    漫谈使用Kafka作为MQ中间件
    数据库事务隔离引发的关于锁机制的思考
    使用Redis作为高速缓存
    Docker 构建映像
    Centos7 Nginx开机启动
    Docker 设置固定网络IP
    CentOS docker 常用命令
  • 原文地址:https://www.cnblogs.com/psimonw/p/11832697.html
Copyright © 2020-2023  润新知