【XSY3344】连续段 DP 牛顿迭代 NTT
题目大意
对于一个长度为 \(n\) 的排列 \(p\),我们称一个区间 \([l,r]\) 是连续的当且仅当 \((\max_{l\leq i\leq r}a_i)-(\min_{l\leq i\leq r}a_i)=r-l\)。
对于两个排列 \(p_1,p_2\),我们称这两个排列是等价的,当且仅当他们的长度相同且连续区间的集合相同。
给你 \(n\),对于 \(1\leq i\leq n\),所有求 \(i\) 阶排列形成的等价类个数对 \(p\) 取模的值。
\(n\leq 100000,p=k\times2^{18}+1,k\in \mathbb {N},p\) 是质数。
题解
对于一个区间 \([l,r]\),如果这个区间的所有子区间都是连续的,就称这个区间为强连续区间。
每次我们找一个最大的强连续区间,把这个区间内所有点缩到一起。
如果找不到,就找一个最小的连续区间,把这个区间内所有点缩到一起。
这个过程就形成了一个树结构。
- 总共有 \(n\) 个叶子。
- 对于一个代表强连续区间的点,这个点的儿子个数 \(\geq 2\)。
- 对于一个代表连续区间的点,这个点的儿子个数 \(\geq 4\)。(可以发现,长度为 \(\leq 3\) 的序列是一定有强连续区间的。)
每一个排列 \(p\) 对应了一棵树。
对于一棵树,可以构造出一个排列 \(p\)。
那么就只用对这棵树计数了。
显然可以 \(O(n^2)\) DP。
记 \(f_i\) 为长度为 \(i\) 的排列的方案数,\(F(x)=\sum_{i\geq 0}f_ix^i\),那么就有:
\]
用牛顿迭代法解这个方程即可。
G(y)=(y^2+y^4)\frac{1}{1-y}-y+x\\
G'(y)=(2y+4y^3)\frac{1}{1-y}+(y^2+y^4)(-\frac{1}{(1-y)^2})(-1)-1\\
G(F(x))\equiv 0\pmod {x^n}\\
G(F(x))\equiv G(F_0(x))+G'(F_0(x))(F(x)-F_0(x)) \pmod {x^n}\\
F(x)\equiv F_0(x)-\frac{G(F_0(x))}{G'(F_0(x))}\pmod {x^n}\\
\]
时间复杂度:\(O(n\log n)\)
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<functional>
#include<cmath>
#include<vector>
#include<assert.h>
//using namespace std;
using std::min;
using std::max;
using std::swap;
using std::sort;
using std::reverse;
using std::random_shuffle;
using std::lower_bound;
using std::upper_bound;
using std::unique;
using std::vector;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef std::pair<int,int> pii;
typedef std::pair<ll,ll> pll;
void open(const char *s){
#ifndef ONLINE_JUDGE
char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
void open2(const char *s){
#ifdef DEBUG
char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
int rd(){int s=0,c,b=0;while(((c=getchar())<'0'||c>'9')&&c!='-');if(c=='-'){c=getchar();b=1;}do{s=s*10+c-'0';}while((c=getchar())>='0'&&c<='9');return b?-s:s;}
void put(int x){if(!x){putchar('0');return;}static int c[20];int t=0;while(x){c[++t]=x%10;x/=10;}while(t)putchar(c[t--]+'0');}
int upmin(int &a,int b){if(b<a){a=b;return 1;}return 0;}
int upmax(int &a,int b){if(b>a){a=b;return 1;}return 0;}
const int N=270000;
ll p,g;
ll fp(ll a,ll b)
{
ll s=1;
for(;b;b>>=1,a=a*a%p)
if(b&1)
s=s*a%p;
return s;
}
namespace ntt
{
const int W=262144;
int rev[N];
int *w[20];
void init()
{
ll s=fp(g,(p-1)/W);
w[18]=new int[1<<17];
w[18][0]=1;
for(int i=1;i<W/2;i++)
w[18][i]=w[18][i-1]*s%p;
for(int i=17;i>=1;i--)
{
w[i]=new int[1<<(i-1)];
for(int j=0;j<1<<(i-1);j++)
w[i][j]=w[i+1][j<<1];
}
}
void ntt(ll *a,int n,int t)
{
for(int i=1;i<n;i++)
{
rev[i]=(rev[i>>1]>>1)|(i&1?n>>1:0);
if(rev[i]>i)
swap(a[i],a[rev[i]]);
}
for(int i=2,l=1;i<=n;i<<=1,l++)
for(int j=0;j<n;j+=i)
for(int k=0;k<i/2;k++)
{
ll u=a[j+k];
ll v=a[j+k+i/2]*w[l][k];
a[j+k]=(u+v)%p;
a[j+k+i/2]=(u-v)%p;
}
if(t==-1)
{
reverse(a+1,a+n);
ll inv=fp(n,p-2);
for(int i=0;i<n;i++)
a[i]=a[i]*inv%p;
}
}
void add(ll *a,ll *b,ll *c,int n,int m,int l)
{
static ll a1[N];
int k=max(n,m);
for(int i=0;i<=k;i++)
a1[i]=0;
for(int i=0;i<=n;i++)
a1[i]=(a1[i]+a[i])%p;
for(int i=0;i<=m;i++)
a1[i]=(a1[i]+b[i])%p;
for(int i=0;i<=l;i++)
c[i]=a1[i];
}
void mul(ll *a,ll *b,ll *c,int n,int m,int l)
{
static ll a1[N],a2[N];
int k=1;
while(k<=n+m)
k<<=1;
for(int i=0;i<k;i++)
a1[i]=a2[i]=0;
for(int i=0;i<=n;i++)
a1[i]=a[i];
for(int i=0;i<=m;i++)
a2[i]=b[i];
ntt(a1,k,1);
ntt(a2,k,1);
for(int i=0;i<k;i++)
a1[i]=a1[i]*a2[i]%p;
ntt(a1,k,-1);
for(int i=0;i<=l;i++)
c[i]=(a1[i]+p)%p;
}
void getinv(ll *a,ll *b,int n)
{
if(n==1)
{
b[0]=fp(a[0],p-2);
return;
}
getinv(a,b,n>>1);
static ll a1[N],a2[N];
for(int i=0;i<n<<1;i++)
a1[i]=a2[i]=0;
for(int i=0;i<n;i++)
a1[i]=a[i];
for(int i=0;i<n>>1;i++)
a2[i]=b[i];
ntt(a1,n<<1,1);
ntt(a2,n<<1,1);
for(int i=0;i<n<<1;i++)
a1[i]=a2[i]*(2-a1[i]*a2[i]%p)%p;
ntt(a1,n<<1,-1);
for(int i=0;i<n;i++)
b[i]=(a1[i]+p)%p;
}
}
int e[50],t;
int check(int x)
{
for(int i=1;i<=t;i++)
if(fp(x,(p-1)/e[i])==1)
return 0;
return 1;
}
void getg()
{
int _=p-1;
for(int i=2;i*i<=_;i++)
if(_%i==0)
{
e[++t]=i;
while(_%i==0)
_/=i;
}
if(_!=1)
e[++t]=_;
for(int i=1;;i++)
if(check(i))
{
g=i;
return;
}
}
void solve(ll *a,int n)
{
if(n==1)
return;
solve(a,n>>1);
static ll f1[N],f2[N],f3[N],f4[N],f5[N],a1[N],a2[N],a3[N],a4[N];
for(int i=0;i<n;i++)
a1[i]=(p-a[i])%p;
a1[0]++;
for(int i=0;i<n;i++)
f1[i]=a[i];
ntt::ntt(f1,n<<1,1);
for(int i=0;i<n<<1;i++)
f2[i]=f1[i]*f1[i]%p;
ntt::ntt(f2,n<<1,-1);
for(int i=0;i<n;i++)
f4[i]=f2[i];
ntt::ntt(f4,n<<1,1);
for(int i=0;i<n<<1;i++)
f3[i]=f4[i]*f1[i]%p;
ntt::ntt(f3,n<<1,-1);
for(int i=0;i<n<<1;i++)
f4[i]=f4[i]*f4[i]%p;
ntt::ntt(f4,n<<1,-1);
for(int i=0;i<n;i++)
f5[i]=f4[i];
ntt::ntt(f5,n<<1,1);
for(int i=0;i<n<<1;i++)
f5[i]=f5[i]*f1[i]%p;
ntt::ntt(f5,n<<1,-1);
for(int i=0;i<n;i++)
f1[i]=a[i];
for(int i=0;i<n;i++)
a1[i]=(-f1[i]+3*f2[i]-2*f3[i]+f4[i]-f5[i])%p;
for(int i=1;i<n;i++)
a1[i]=(a1[i]-2*f1[i-1]+f2[i-1])%p;
a1[1]++;
for(int i=0;i<n;i++)
a2[i]=(4*f1[i]-2*f2[i]+4*f3[i]-3*f4[i])%p;
a2[0]--;
ntt::getinv(a2,a3,n);
ntt::mul(a1,a3,a4,n-1,n-1,n-1);
for(int i=0;i<n;i++)
a[i]=(a[i]-a4[i]+p)%p;
}
int n;
ll s[N];
int main()
{
open("b");
scanf("%d%lld",&n,&p);
getg();
ntt::init();
int k=1;
while(k<=n)
k<<=1;
solve(s,k);
for(int i=1;i<=n;i++)
{
s[i]=(s[i]+p)%p;
printf("%lld\n",s[i]);
}
return 0;
}
【XSY3344】连续段 DP 牛顿迭代 NTT的更多相关文章
- 【XSY2680】玩具谜题 NTT 牛顿迭代
题目描述 小南一共有\(n\)种不同的玩具小人,每种玩具小人的数量都可以被认为是无限大.每种玩具小人都有特定的血量,第\(i\)种玩具小人的血量就是整数\(i\).此外,每种玩具小人还有自己的攻击力, ...
- 【loj6538】烷基计数 加强版 加强版 Burnside引理+多项式牛顿迭代
别问我为啥突然刷了道OI题,也别问我为啥花括号不换行了... 题目描述 求含 $n$ 个碳原子的本质不同的烷基数目模 $998244353$ 的结果.$1\le n\le 10^5$ . 题解 Bur ...
- 牛顿迭代,多项式求逆,除法,开方,exp,ln,求幂
牛顿迭代 若 \[G(F_0(x))\equiv 0(mod\ x^{2^t})\] 牛顿迭代 \[F(x)\equiv F_0(x)-\frac{G(F_0(x))}{G'(F_0(x))}(mod ...
- test20181219 连续段的期望
题意 连续段的期望 [问题描述] 小N最近学习了位运算,她发现2个数xor之后数的大小可能变大也可能变小,and之后都不会变大,or之后不会变小.于是她想算出以下的期望值:现在有 N个数排成一排,如果 ...
- LOJ #6538. 烷基计数 加强版 加强版(生成函数,burnside引理,多项式牛顿迭代)
传送门. 不妨设\(A(x)\)表示答案. 对于一个点,考虑它的三个子节点,直接卷起来是\(A(x)^3\),但是这样肯定会计重,因为我们要的是无序的子节点. 那么用burnside引理,枚举一个排列 ...
- luogu P4726 【模板】多项式指数函数 多项式 exp 牛顿迭代 泰勒展开
LINK:多项式 exp 做多项式的题 简直在嗑药. 前置只是 泰勒展开 这个东西用于 对于一个函数f(x) 我们不好得到 其在x处的取值. 所以另外设一个函数g(x) 来在x点处无限逼近f(x). ...
- 区间的连续段~ST表(模板题)
链接:https://www.nowcoder.com/acm/contest/82/B来源:牛客网 时间限制:C/C++ 7秒,其他语言14秒 空间限制:C/C++ 262144K,其他语言5242 ...
- HDU.2899.Strange fuction(牛顿迭代)
题目链接 \(Description\) 求函数\(F(x)=6\times x^7+8\times x^6+7\times x^3+5\times x^2-y\times x\)在\(x\in \l ...
- HDU 1231.最大连续子序列-dp+位置标记
最大连续子序列 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Sub ...
随机推荐
- leetcode math类型题目解题总结
2. Add Two Numbers https://leetcode.com/problems/add-two-numbers/description/ class Solution { publi ...
- 在表单提交之前做校验-利用jQuery的submit方法
点击表单中的提交按钮,默认就会提交表单,如果要在表单提交之前做一些校验,那么就可以用jQuery的submit方法. 也就是jQuery的submit的方法执行顺序,在表单的提交之前.用法如下: $( ...
- 数据库学习(MySQL):JDBC的简单增删改查实现
本文为原创,转载请注明出处: https://www.cnblogs.com/Tom-shushu/p/9171896.html 这里我们先在数据库建立一个userinfo表: CREATE TABL ...
- jsp内置对象-application对象
隐含对象application是javax.servlet.ServletContext接口实现类的对象,其拥有application的作用范围,即application可以用于在多个用户之间保存数据 ...
- Easyui 关闭jquery-easui tab标签页前触发事件
关闭jquery-easui tab标签页前触发事件 by:授客 QQ:1033553122 测试环境 jquery-easyui-1.5.3 需求场景 点击父页面tab 页关闭按钮时,需要做判断,判 ...
- Android SingleTask使用注意点
在ActivityA中,startActivityForResult到ActivityB,其中ActivityB设置为SingleTask. 那么在实际出现的现象为: ActivityA的onActi ...
- 轻松学习UML之用例图,时序图
本文主要讲解UML图中的用例图(Use Case Diagram)和时序图(Sequence Diagram)相关内容,如有不足之处,还请指正. 概述 统一建模语言(UML,UnifiedModeli ...
- git排除插件(.ignore)配置
# Created by .ignore support plugin (hsz.mobi) ### Maven template target/ ### JetBrains template # C ...
- C# 得到EXCEL表格中的有效行数和列数
每种方法中上面的是Excel的行数,下面的是Excel的列数.方法七:经过加工修改已经可以读出来的是有效数据行 using Excel = Microsoft.Office.Interop.Excel ...
- python3 判断字符串是否为纯空格组成的方法
1. isspace()方法 定义一个值为3个空格的变量,使用isspace()方法可直接判断该字符串是否全为空格组成. >>> s = ' ' >>> s.iss ...