【UOJ #34】多项式乘法
http://uoj.ac/problem/34
看了好长时间的FFT和NTT啊qwq在原根那块磨蹭了好久_(:з」∠)_
首先设答案多项式的长度拓展到2的幂次后为n,我们只要求出一个g(不是原根)满足\(i\in \{1\dots n\},g^i\)互不相同,且\(g^n=1\)。
把这个g当做“FFT里面的主n次单位根”的类似物。
而且\(g^{\frac n2}=-1\),因为\(g^{\frac n2}\)与\(g^n\)不相同且\((g^{\frac n2})^2=g^n=1\),所以\(g^{\frac n2}\)只能是-1。
剩下的只要选一个够大的模数满足答案多项式的所有系数都小于这个模数就可以了。
我选的模数是998244353(\(7×17×2^{23}+1\),一个质数,UOJ模数)。不是所有的模数p都可以,像\(10^9+7\)就不可以,因为此时p-1的因子2的指数不够大。只有p-1的因子2的指数c足够大,\(2^c>n\)时才可以。
这里我写了一个暴力找到了一个g=646。
//998244353
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int p = 998244353;
const int n = 1048576;
bool can[p];
int main() {
bool flag = false;
for (int num = 1; num < p; ++num) {
ll re = num;
flag = true;
for (int i = 1; i <= n; ++i) {
if (can[re]) {flag = false; break;}
can[re] = true;
re = re * num % p;
}
if (!flag || re != num) {
re = num;
for (int i = 1; i <= n; ++i) {
if (can[re]) can[re] = false;
re = re * num % p;
}
continue;
}
printf("%d\n", num);
return 0;
}
/*
freopen("tab.txt", "w", stdout);
int num = 646; ll ret = 1;
for (int i = 1; i <= (n >> 1); ++i) {
ret = ret * num % p;
printf("%d %I64d \n", i, ret);
}
*/
}
求出g后就可以NTT了,不过也需要预处理一些分治实现NNT时(一般是迭代实现,这里也是)n不断除2变小需要用到的不同的“主n次单位根”和“主n次单位根的逆元”。
一开始我对原根(及主n次单位根)的定义比较模糊,没有预处理“主n次单位根”的逆元而直接用负的“主n次单位根”导致逆DNNT出错qwq
NTT有取模果然慢啊,不过没有FFT的复数精度误差。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int p = 998244353;
const int N = 1048576;
const int g = 646;
int rev[N], WN[23], nWN[23], n;
int ipow(int a, int b) {
int ret = 1, w = a;
while (b) {
if (b & 1) ret = 1ll * ret * w % p;
w = 1ll * w * w % p;
b >>= 1;
}
return ret;
}
void DNT(int *a, int *A, int flag) {
for (int i = 0; i < n; ++i) A[rev[i]] = a[i];
int tmp = 1;
for (int m = 2; m <= n; m <<= 1, ++tmp) {
int mid = m >> 1, wn = flag == 1 ? WN[tmp] : nWN[tmp];
for (int i = 0; i < n; i += m) {
int w = 1;
for (int j = 0; j < mid; ++j) {
int t = A[i + j], u = 1ll * A[i + j + mid] * w % p;
A[i + j] = (t + u) % p;
A[i + j + mid] = (t - u + p) % p;
w = 1ll * w * wn % p;
}
}
}
if (flag == -1) {
int ni = ipow(n, p - 2);
for (int i = 0; i < n; ++i)
A[i] = 1ll * A[i] * ni % p;
}
}
int da[N], db[N], dc[N];
void NTT(int *a, int lena, int *b, int lenb, int *ans, int n) {
DNT(a, da, 1); DNT(b, db, 1);
for (int i = 0; i < n; ++i) dc[i] = 1ll * da[i] * db[i] % p;
DNT(dc, ans, -1);
}
void init() {
WN[20] = g; nWN[20] = ipow(g, p - 2);
for (int i = 19; i >= 1; --i) {
WN[i] = 1ll * WN[i + 1] * WN[i + 1] % p;
nWN[i] = ipow(WN[i], p - 2);
}
int num = n, tot = 0, res;
while (num) {++tot; num >>= 1;}
n = 1 << tot;
for (int i = 0; i < n; ++i) {
num = i; res = 0;
for (int j = tot - 1; j >= 0; --j) {
if (num & 1) res |= (1 << j);
num >>= 1;
}
rev[i] = res;
}
}
int lena, lenb, a[N >> 1], b[N >> 1], ans[N];
int main() {
scanf("%d%d", &lena, &lenb); ++lena; ++lenb;
for (int i = 0; i < lena; ++i) scanf("%d", a + i);
for (int i = 0; i < lenb; ++i) scanf("%d", b + i);
n = lena + lenb - 1;
init();
NTT(a, lena, b, lenb, ans, n);
int totlen = lena + lenb - 1;
for (int i = 0; i < totlen; ++i) printf("%d ", ans[i]);
puts("");
return 0;
}
一个板子都写了这么长时间省选是要滚粗吗→_→
【UOJ #34】多项式乘法的更多相关文章
- [UOJ#34]多项式乘法
[UOJ#34]多项式乘法 试题描述 这是一道模板题. 给你两个多项式,请输出乘起来后的多项式. 输入 第一行两个整数 n 和 m,分别表示两个多项式的次数. 第二行 n+1 个整数,分别表示第一个多 ...
- ●UOJ 34 多项式乘法
题链: http://uoj.ac/problem/34 题解: FFT入门题. (终于接触到迷一样的FFT了) 初学者在对复数和单位根有简单了解的基础上,可以直接看<再探快速傅里叶变换> ...
- UOJ#34. 多项式乘法(NTT)
这是一道模板题. 给你两个多项式,请输出乘起来后的多项式. 输入格式 第一行两个整数 nn 和 mm,分别表示两个多项式的次数. 第二行 n+1n+1 个整数,表示第一个多项式的 00 到 nn 次项 ...
- 【刷题】UOJ #34 多项式乘法
这是一道模板题. 给你两个多项式,请输出乘起来后的多项式. 输入格式 第一行两个整数 \(n\) 和 \(m\) ,分别表示两个多项式的次数. 第二行 \(n+1\) 个整数,表示第一个多项式的 \( ...
- UOJ 34 多项式乘法 FFT 模板
这是一道模板题. 给你两个多项式,请输出乘起来后的多项式. 输入格式 第一行两个整数 nn 和 mm,分别表示两个多项式的次数. 第二行 n+1n+1 个整数,表示第一个多项式的 00 到 nn 次项 ...
- 2018.11.14 uoj#34. 多项式乘法(ntt)
传送门 今天学习nttnttntt. 其实递归方法和fftfftfft是完全相同的. 只不过fftfftfft的单位根用的是复数中的东西,而nttnttntt用的是数论里面有相同性质的原根. 代码: ...
- 2018.11.14 uoj#34. 多项式乘法(fft)
传送门 NOIpNOIpNOIp爆炸不能阻止我搞oioioi的决心 信息技术课进行一点康复训练. fftfftfft板题. 代码: #include<bits/stdc++.h> usin ...
- UOJ 34 多项式乘法 ——NTT
[题目分析] 快速数论变换的模板题目. 与fft的方法类似,只是把复数域中的具有循环性质的单位复数根换成了模意义下的原根. 然后和fft一样写就好了,没有精度误差,但是跑起来比较慢. 这破题目改了好长 ...
- UOJ 34: 多项式乘法(FFT模板题)
关于FFT 这个博客的讲解超级棒 http://blog.miskcoo.com/2015/04/polynomial-multiplication-and-fast-fourier-transfor ...
- [UOJ 0034] 多项式乘法
#34. 多项式乘法 统计 描述 提交 自定义测试 这是一道模板题. 给你两个多项式,请输出乘起来后的多项式. 输入格式 第一行两个整数 nn 和 mm,分别表示两个多项式的次数. 第二行 n+1n+ ...
随机推荐
- modelsim10 SE 仿真lattice Xp2工程
1.首先要建立Lattice XP2库 在modelsim10 SE启动后.首先指定Lattice Diamond 1.4 给定的仿真器库源代码编译目录: C:\lscc\diamond\1.4\ca ...
- iOS多线程
iOS开发Demo(示例程序)源代码
本系列所有开发文档翻译链接地址:iOS7开发-Apple苹果iPhone开发Xcode官方文档翻译PDF下载地址(2013年12月29日更新版) iOS程序源代码下载链接:01.大任务.zip22 ...
- BTA 常问的 Java基础40道常见面试题及详细答案(山东数漫江湖))
八种基本数据类型的大小,以及他们的封装类 引用数据类型 Switch能否用string做参数 equals与==的区别 自动装箱,常量池 Object有哪些公用方法 Java的四种引用,强弱软虚,用到 ...
- Spring cookie 实战(山东数漫江湖)
Cookie是什么 简单来说,cookie就是浏览器储存在用户电脑上的一小段文本文件.cookie 是纯文本格式,不包含任何可执行的代码.一个web页面或服务器告知浏览器按照一定规范来储存这些信息,并 ...
- Linux汇编教程02:编写第一个汇编程序
学习一门语言,最好的方式就是在运用中学习,那么在这一章节中,我们开始编写我们的第一个汇编程序.当然作为第一个程序,其实十分的简单,但可以给大家一个基本的轮廓,了解汇编大概是这样的. 我们这个程序实际上 ...
- WAMP Apache 2.5 配置虚拟主机
1.在 Apache 的安装目录下 conf/httpd.conf 文件中搜索 hosts,去掉 Include 前面的 “#” 号后,即可启用虚拟主机. # Virtual hosts #Inclu ...
- (二十)ubuntu的recovery mode解决用户一些实际问题
遇到的问题如下: 1.在当前用户下使用sudo来直接修改password等几个文件,一旦修改了passwd,用户名发生了变化,其他的用户组.密码等却没有对应的配置,就再进不了该用户了. 2.忘记用户密 ...
- hadoop安装 伪分布
伪分布hadoop 安装总结 准备,在配置中hadoop用的9000端口,如果有其它软件用着这个端口,建议更换后再进行下面配置,以避免出现错误.比如php-fpm经常使用9000端口. 一.下载jdk ...
- [ python ] 项目二:主机批量管理程序
开发要求: 1. 对主机进行批量管理 2. 可对单台或主机组批量执行命令 3. 可上传文件到对应的主机或组 4. 使用多线程实现 程序: 1. README # 作者:hkey # ...
- MapReduce案例二:好友推荐
1.需求 推荐好友的好友 图1: 2.解决思路 3.代码 3.1MyFoF类代码 说明: 该类定义了所加载的配置,以及执行的map,reduce程序所需要加载运行的类 package com.hado ...