luogu5282 【模板】快速阶乘算法
由于巨佬 shadowice1984 卡时限,本代码已经 T 请不要粘上去交
退役之后再写一个常数小的多项式取模吧
一句话题意:NP问题,求N!%P
吐槽:出题人太毒瘤...必须写任意模数NTT,而且加法取模还溢出...
我常数太大,粘的好久以前写的多项式取模,卡了卡常才A,大家1e3 1e4不要写vector,不要参考下面的代码
orz shadowice1984 写 \(O(\sqrt n\log n)\) 吊打我的 \(O(\sqrt n\log^2 n)\)
以下是 \(O(\sqrt n\log^2 n)\) 的题解
前置芝士: 多项式多点求值、多项式取模、多项式求逆
出门左转你谷模板区,包教不包会
前置芝士: 任意模数NTT
出门左转你谷模板区,包教不包会
本题题解
首先我们发现p是2^31-1的
你可以考虑像分段打表那样根号分块,把1~p分成 \(O(\sqrt p)\) 份
然后你求出每一份的值来,最后边角暴力就行了
那么怎么求呢
你会发现第一块是 \((1*2*...*s)\), 第二块是 \(((s+1)*(s+2)*...*(s+s))\)
第i块就是 \((s(i-1)+1)*(s(i-1)+2)*(s(i-1)+3)*(s(i-1)+s)\)
我们发现这是一个关于i的多项式,可以用分治+NTT在 \(O(\sqrt p \log^2p)\)的时间内求出这个多项式
然后你要求出第i=1...s的每一个数的值,也就是每一块数的积,你会发现是一个多项式多点求值,复杂度也是\(O(\sqrt p\log ^2p)\)
直接去隔壁模板区把多项式多点求值板子粘过来就行了
由于出题人故意卡模数,需要把FFT换成任意模数NTT...
然后你就在线A题了...
代码太丑,用vector xjb写的
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n, p, s;
const int sb = 32768, sb2 = 1073741824;
const double pi = acos(-1);
int qpow(int x, int y)
{
int res = 1;
for (x %= p; y > 0; y >>= 1, x = x * (long long)x % p)
if (y & 1) res = res * (long long)x % p;
return res;
}
struct Complex { double real, imag; Complex(double r = 0, double i = 0) : real(r), imag(i) { } };
Complex a1[600000], a2[600000], b1[600000], b2[600000], a1b1[600000], ab[600000], a2b2[600000];
Complex operator+(const Complex &a, const Complex &b) { return Complex(a.real + b.real, a.imag + b.imag); }
Complex operator-(const Complex &a, const Complex &b) { return Complex(a.real - b.real, a.imag - b.imag); }
Complex operator*(const Complex &a, const Complex &b) { return Complex(a.real * b.real - a.imag * b.imag, a.real * b.imag + b.real * a.imag); }
Complex *w[22];
Complex getw(int x, int y, int falg) { return Complex(w[x][y].real, falg * w[x][y].imag); }
int *r[22];
void fftinit()
{
for (int i = 0; i < 19; i++)
{
w[i] = new Complex[1 << i], r[i] = new int[1 << i];
for (int j = 0; j < (1 << i); j++) w[i][j] = Complex(cos(pi * j / (1 << i)), sin(pi * j / (1 << i)));
r[i][0] = 0;
for (int j = 1; j < (1 << i); j++) r[i][j] = (r[i][j >> 1] >> 1) | ((j & 1) * (1 << (i - 1)));
}
}
void fft(Complex *a, int len, int loglen, int falg)
{
Complex w, t;
for (int i = 0; i < len; i++) if (r[loglen][i] < i) swap(a[i], a[r[loglen][i]]);
for (int i = 1, logi = 0; i < len; logi++, i <<= 1) for (int j = 0; j < len; j += i << 1) for (int k = 0; k < i; k++)
w = getw(logi, k, falg), t = a[j + k + i] * w, a[j + k + i] = a[j + k] - t, a[j + k] = a[j + k] + t;
if (falg == -1) for (int i = 0; i < len; i++) a[i].real /= len, a[i].imag /= len;
}
int toint(Complex x) { return (((long long)(round(x.real) + 0.5)) % p + p) % p; }
vector<int> operator*(vector<int> a, vector<int> b)
{
int len = 1, loglen = 0; int sz = a.size() + b.size() - 1; while (len < sz) len <<= 1, loglen++;
a.resize(len), b.resize(len);
vector<int> res;
for (int i = 0; i < len; i++) a1[i] = a[i] / sb, a2[i] = a[i] % sb, b1[i] = b[i] / sb, b2[i] = b[i] % sb;
fft(a1, len, loglen, 1), fft(a2, len, loglen, 1), fft(b1, len, loglen, 1), fft(b2, len, loglen, 1);
for (int i = 0; i < len; i++) a1b1[i] = a1[i] * b1[i], ab[i] = a1[i] * b2[i] + a2[i] * b1[i], a2b2[i] = a2[i] * b2[i];
fft(a1b1, len, loglen, -1), fft(ab, len, loglen, -1), fft(a2b2, len, loglen, -1);
for (int i = 0; i < len; i++)
res.push_back(((toint(a1b1[i]) * (long long)sb2 % p + toint(ab[i]) * (long long)sb % p) % p + toint(a2b2[i])) % p);
res.resize(sz);
return res;
}
vector<int> operator+(vector<int> a, vector<int> b)
{
vector<int> res; res.resize(max(a.size(), b.size()));
a.resize(res.size()); b.resize(res.size());
for (int i = 0; i < (int)res.size(); i++) res[i] = (a[i] + b[i]) % p;
return res;
}
vector<int> operator-(vector<int> a, vector<int> b)
{
vector<int> res; res.resize(max(a.size(), b.size()));
a.resize(res.size()); b.resize(res.size());
for (int i = 0; i < (int)res.size(); i++) res[i] = ((a[i] - b[i]) % p + p) % p;
return res;
}
vector<int> poly_inv(vector<int> a)
{
if (a.size() == 1) { a[0] = qpow(a[0], p - 2); return a; }
int n = a.size(), newsz = (n + 1) >> 1;
vector<int> b(a); b.resize(newsz); b = poly_inv(b);
vector<int> c(a * b);
for (int &i: c) i = (p - i) % p;
c[0] = (c[0] + 2) % p; a = c * b; a.resize(n); return a;
}
// vector<int> poly_r(vector<int> a) { reverse(a.begin(), a.end()); return a; }
void div(vector<int> f, vector<int> g, vector<int> &q, vector<int> &r)
{
int n = f.size() - 1, m = g.size() - 1;
vector<int> gr = g; reverse(gr.begin(), gr.end());
gr.resize(n - m + 1);
q = f;
reverse(q.begin(), q.end());
q = q * poly_inv(gr);
q.resize(n - m + 1);
reverse(q.begin(), q.end());
r = f - g * q;
r.resize(m);
// vector<int> gq = g * q;
// r.resize(m);
// gq.resize(m);
// f.resize(m);
// for (int i = 0; i < m; i++)
// r[i] = ((f[i] - gq[i]) % p + p) % p;
}
vector<int> zz[200010];
int res[100010];
vector<int> fuck(int l, int r)
{
if (l == r) { vector<int> res; res.push_back(l), res.push_back(s); return res; }
int mid = (l + r) / 2;
return fuck(l, mid) * fuck(mid + 1, r);
}
void prework(int x, int cl, int cr)
{
if (cl == cr)
{
zz[x].push_back((p - cl) % p), zz[x].push_back(1);
return;
}
int mid = (cl + cr) / 2;
prework(x * 2, cl, mid), prework(x * 2 + 1, mid + 1, cr);
zz[x] = zz[x * 2] * zz[x * 2 + 1];
}
void work(int x, int cl, int cr, vector<int> poly)
{
if (cr - cl <= 400)
{
int sb = poly.size();
for (int t = cl; t <= cr; t++)
{
int tmp = 1;
for (int i = 0; i < sb; i++)
res[t] = (res[t] + tmp * (long long)poly[i] % p) % p, tmp = tmp * (long long)t % p;
}
return;
}
vector<int> tmp, rel, rer;
div(poly, zz[x * 2], tmp, rel);
div(poly, zz[x * 2 + 1], tmp, rer);
int mid = (cl + cr) / 2;
work(x * 2, cl, mid, rel), work(x * 2 + 1, mid + 1, cr, rer);
}
signed main()
{
fftinit();
scanf("%lld%lld", &n, &p);
// n = 998244353, p = 2147483647;
s = sqrt(p) + 1;
vector<int> poly = fuck(1, s);
prework(1, 0, s);
// printf("prework ok\n");
work(1, 0, s, poly);
// printf("work ok\n");
int ans = 1;
for (int i = n / s * s + 1; i <= n; i++) ans = ans * (long long)i % p;
for (int i = 0; i < n / s; i++) ans = ans * (long long)res[i] % p;
printf("%lld\n", ans);
return 0; //拜拜程序~
}
luogu5282 【模板】快速阶乘算法的更多相关文章
- P5282 【模板】快速阶乘算法(多项式运算+拉格朗日插值+倍增)
题面 传送门 前置芝士 优化后的\(MTT\)(四次\(FFT\)) 题解 这里有多点求值的做法然而被\(shadowice\)巨巨吊起来打了一顿,所以来学一下倍增 成功同时拿到本题最优解和最劣解-- ...
- 洛谷P5282 【模板】快速阶乘算法(多项式多点求值+MTT)
题面 传送门 前置芝士 \(MTT\),多项式多点求值 题解 这题法老当初好像讲过--而且他还说这种题目如果模数已经给定可以直接分段打表艹过去 以下是题解 我们设 \[F(x)=\prod_{i=0} ...
- JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(四):自定义T4模板快速生成页面
前言:上篇介绍了下ko增删改查的封装,确实节省了大量的js代码.博主是一个喜欢偷懒的人,总觉得这些基础的增删改查效果能不能通过一个什么工具直接生成页面效果,啥代码都不用写了,那该多爽.于是研究了下T4 ...
- sdut 1592转置矩阵【稀疏矩阵的压缩存储】【快速转置算法】
转置矩阵 Time Limit: 1000ms Memory limit: 32768K 有疑问?点这里^_^ 题目链接:http://acm.sdut.edu.cn/sdutoj/proble ...
- 深度信任网络的快速学习算法(Hinton的论文)
也没啥原创,就是在学习深度学习的过程中丰富一下我的博客,嘿嘿. 不喜勿喷! Hinton是深度学习方面的大牛,跟着大牛走一般不会错吧-- 来源:A fast learning algorithm fo ...
- Java 实现阶乘算法
阶乘算法如下: 以下列出 0 至 20 的阶乘: 0!=1,(0 的阶乘是存在的) 1!=1, 2!=2, 3!=6, 4!=24, 5!=120, 6!=720, 7!=5040, 8!=40320 ...
- bzoj 3283: 运算器 扩展Baby Step Giant Step && 快速阶乘
3283: 运算器 Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 184 Solved: 59[Submit][Status][Discuss] D ...
- Gamma原理及快速实现算法(C/C++)(转)
源:Gamma原理及快速实现算法(C/C++) 原文:http://blog.csdn.net/lxy201700/article/details/24929013 参考 http://www.cam ...
- ------ 新春第一炮:阶乘算法性能分析与 double fault 蓝屏故障排查 Part I ------
-------------------------------------------------------------------------- 春节期间闲来无事想研究下算法,上机测试代码却遇到了 ...
随机推荐
- 785. Is Graph Bipartite?从两个集合中取点构图
[抄题]: Given an undirected graph, return true if and only if it is bipartite. Recall that a graph is ...
- java中的不死兔问题(斐波那契数列)(递归思想)
有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? public class Item { public static ...
- spring mabatis springmvc 看过
.Spring中AOP的应用场景.Aop原理.好处? 答:AOP--Aspect Oriented Programming面向切面编程:用来封装横切关注点,具体可以在下面的场景中使用: Authent ...
- 使用python把图片存入数据库-乾颐堂
一般情况下我们是把图片存储在文件系统中,而只在数据库中存储文件路径的,但是有时候也会有特殊的需求:把图片二进制存入数据库. 今天我们采用的是python+mysql的方式 MYSQL 是支持把图片存入 ...
- SpringMVC源码解读 - HandlerMapping - AbstractUrlHandlerMapping系列request分发
AbstractHandlerMapping实现HandlerMapping接口定的getHandler 1. 提供getHandlerInternal模板方法给子类实现 2. 如果没有获取Handl ...
- BZOJ 1977 严格次小生成树
小C最近学了很多最小生成树的算法,Prim算法.Kurskal算法.消圈算法等等.正当小C洋洋得意之时,小P又来泼小C冷水了.小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小 ...
- 通俗理解java序列化
1 序列化是干什么的呢? 搬家的 简单说就是为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来.虽然你可以用你自己的各种各样的方法来保存object sta ...
- Linq转换操作之OfType,Cast,AsEnumerable,ToLookup源码分析
Linq转换操作之OfType,Cast,AsEnumerable,ToLookup源码分析 一:Tolookup 1. 从方法的注解上可以看到,ToLookup也是一个k,v的形式,那么问题来了,它 ...
- Android-动态添加控件到ScrollView
在实际开发过程中,会需要动态添加控件到ScrollView,就需要在Java代码中,找到ScrollView的孩子(ViewGroup),进行添加即可. Layout: <?xml versio ...
- 【C#】事件
前言:CLR事件模式建立在委托的基础上,委托说调用回调方法的一种类型安全的方式. 我个人觉得事件本质就是委托,所以把委托弄清楚,只要知道事件基本语法就会使用了(如果说到线程安全,我个人觉得这个应该和线 ...