Codeforces 848E - Days of Floral Colours(分治 FFT)
神仙 D1E,一道货真价实的 *3400 %%%%%%%%%%%%
首先注意到一点,由于该图为中心对称图形,\(1\sim n\) 的染色情况一定与 \(n+1\sim 2n\) 的染色情况完全相同,也就是说我们只需考虑 \(1\sim n\) 的染色情况,\(n+1\sim 2n\) 部分各段的长度一定与 \(1\sim n\) 部分的长度完全相同,权值平方一下即可。
我们不妨假设 \(1\) 与 \(n+1\) 之间连了一条长度为 \(n\) 的边,当然这边有人会担心 \(1\) 与 \(n+1\) 之间如果不存在长度为 \(n\) 的边怎么办,事实上我们可以将其旋转一个角度,这样就可以归约到 \(1\) 与 \(n+1\) 之间存在边的情况了,具体细节将在最后提到。
我们设 \(dp_i\) 为已经确定了花朵 \(1\sim i\) 的连边方式,并且 \(i\) 与 \(i+n\) 之间连了一条长度为 \(n\) 的边的情况下,所有方案的权值之和,考虑转移,简单画个图就可以发现对于任意一对存在边的花朵,可能的图形只有以下四种(这里蒯了神仙 \(\color{black}\texttt{s}\color{red}\texttt{hadowice1984}\) 神仙题解里的图:
稍微解释一下上面这张图,这四幅图分别对应以下四种情况:
- 花朵 \(i\) 与花朵 \(i+n\) 连长度为 \(n\) 的边(即右下角的图)
- 花朵 \(i\) 与花朵 \(i+1\) 连长度为 \(1\) 的边(即左下角的图)
- 花朵 \(i\) 与花朵 \(i+2\) 连长度为 \(2\) 的边,同时花朵 \(i+1\) 与花朵 \(i+3\) 也连长度为 \(2\) 的边(即右上角的图)
- 花朵 \(i\) 与花朵 \(i+2\) 连长度为 \(2\) 的边,同时花朵 \(i+1\) 与花朵 \(i+n+1\) 连长度为 \(n\) 的边(即左上角的图)
前面三种情况都异常容易解决。为了表述方便,我们定义连续段为极长的不存在连了长度为 \(n\) 的边的花朵段,记连续段的长度为这个连续段中花朵的个数 \(+1\),那么长度为 \(m\) 的连续段的权值就是 \((m-1)^2\)。记 \(g_i\) 为长度为 \(i\) 的连续段连边的方案数,那么显然有递推式 \(g_i=g_{i-2}+g_{i-4}\),\(g_0=1\)。预处理 \(g\) 数组之后就可以枚举上一段长度为 \(n\) 的边的位置转移了,即 \(dp_i=\sum\limits_{j=1}^{i-1}dp_jg_{i-j}(i-j-1)^2\)。
繁琐的地方在于最后一种情况,也就是长度为 \(n\) 的边的两边存在长度为 \(2\) 的边的情况。在下文中为了表述方便我们记这种图形为特殊图形。显然加上这种情况之后原来的 \(dp\) 状态就不可行了,因为最后一段存在特殊图形与不存在特殊图形的转移是不同了,此时我们就要在 \(dp\) 状态中引入常数项来区分存在特殊图形与不存在特殊图形的情况。具体来说,对于一个长度为 \(m\) 的连续段,它内部特殊图形的存在情况只可能有四种情况(*):
- 左右部分都不存在特殊图形
- 左边存在特殊图形,右边不存在特殊图形
- 左边不存在特殊图形,右边存在特殊图形
- 左右部分都存在特殊图形
不难发现第一种情况的方案数就是 \(g_{m-1}\),第二、三种情况是等价的,方案数都是 \(g_{m-2}\),因为有一朵花的方案已经确定了,只能在剩余 \(m-2\) 朵花中配对,同理第四种情况方案数是 \(g_{m-3}\)。
了解了这一点之后,我们的方向就明确了。我们在 \(dp\) 状态中新增一维常数,即将原来的 \(dp\) 状态更改为 \(dp_{i,0/1/2}\),表示当前考虑到了第 \(i\) 朵花,并且第 \(i\) 朵花连了长度为 \(n\) 的边,第 \(1\) 朵花与第 \(n\) 朵花周围总共有 \(0/1/2\) 个特殊图形的方案的贡献之和,那么有转移:
- \(dp_{i,0}=g_{i-1}(i-1)^2+\sum\limits_{j=1}^{i-1}dp_{i-j,0}(i-1)^2g_{i-1}+\sum\limits_{j=1}^{i-1}dp_{i-j,1}(i-1)^2g_{i-2}\)
- \(dp_{i,1}=g_{i-2}(i-1)^2+\sum\limits_{j=1}^{i-1}dp_{i-j,0}(i-1)^2g_{i-2}+\sum\limits_{j=1}^{i-1}dp_{i-j,1}(i-1)^2g_{i-3}\)
- \(dp_{i,2}=g_{i-3}(i-1)^2+\sum\limits_{j=1}^{i-1}dp_{i-j,1}(i-1)^2g_{i-2}+\sum\limits_{j=1}^{i-1}dp_{i-j,2}(i-1)^2g_{i-3}\)
稍微解释一下上面的 \(dp\) 转移方程。在 \(dp_{i,0}\) 的转移方程中,前面 \(g_{i-1}(i-1)^2\) 表示 \(1\sim i\) 这 \(i\) 个花朵本身就形成了一个长度为 \(i\) 的连续段,贡献为 \(g_{i-1}(i-1)^2\),否则我们就枚举上一段连续段的长度 \(j\),并分上一个连了长度为 \(n\) 的地方的边出不出现特殊图形两种情况进行转移,如果不出现那就从 \(dp_{i-j,0}\) 转移过来,方案数同(*)处的第一种情况,为 \(g_{i-1}\),再乘上本次划分的贡献 \((i-1)^2\),如果出现那就从 \(dp_{i-j,1}\) 转移过来,方案数同(*)处的第二种情况,为 \(g_{i-2}\),因此我们有 \(dp_{i,0}=g_{i-1}(i-1)^2+\sum\limits_{j=1}^{i-1}dp_{i-j,0}(i-1)^2g_{i-1}+\sum\limits_{j=1}^{i-1}dp_{i-j,1}(i-1)^2g_{i-2}\),\(dp_{i,1},dp_{i,2}\) 的转移方程也同理,自己稍微推推应该可以推出来。这样暴力转移是平方的,不过注意到这样的 \(dp\) 转移方程满足 \(f_{i}=\sum f_{i-j}g_j\) 的形式(我 卷 我 自 己),因此考虑分治 FFT 优化,复杂度 \(n\log^2n\),当然也可以多项式求逆,复杂度少一个 \(\log\),这里就不赘述了。
最后考虑怎样计算答案,分三种情况:
- 如果不存在长度为 \(n\) 的边,这样可能会有一些情况,不过反正贡献都是 \(0\),你还管那么多干嘛呢(
- 如果存在一条长度为 \(n\) 的边,那考虑枚举这条边的一个端点 \(i(i\le n)\),不妨设 \(1\) 为这条边的一个端点,最后乘个 \(n\) 即可。分两种情况,要么 \(1\) 和 \(n+1\) 两边都存在特殊图形,要么都不存在,根据对称性不可能存在一遍有特殊图形一边没有的情况。前者方案数 \(g_{n-1}\),后者方案数 \(g_{n-3}\),再乘上此次划分的贡献 \((n-1)^2\),因此这种情况的答案就是 \((n-1)^2n(g_{n-1}+g_{n-3})\)。
- 如果存在两条及以上长度为 \(n\) 的边,那么我们就枚举 \(1\) 号花朵所在的连续段的长度 \(i\),不妨设 \(1\) 连了长度为 \(n\) 的边,那么显然它可以旋转一个角度,使得 \(1\) 没有连长度为 \(n\) 的边,并且 \(1\) 所在的连续段长度依然为 \(i\),这种旋转的方案数为 \(i\)。接下来考虑怎样计算划分的贡献,还是分是否存在特殊图形考虑,如果 \(1,i\) 两边都不存在特殊图形,那么划分的贡献就是 \(g_{i-1}(i-1)^2f_{n-i,0}\),如果 \(1,i\) 两边都不存在特殊图形,那么划分的贡献是 \(g_{i-3}(i-1)^2f_{n-i,2}\),如果一边存在一边不存在那么划分的方案数为 \(g_{i-2}(i-1)^2f_{n-i,1}\times 2\),把三者加起来再乘个 \(i\) 即可,可得总方案数为 \(i(g_{i-1}(i-1)^2f_{n-i,0}+2g_{i-2}(i-1)^2f_{n-i,1}+g_{i-3}(i-1)^2f_{n-i,2})\)
于是这题就做完了,复杂度 \(n\log^2n\)(如果你有耐心手推多项式求逆那应该也能搞到 \(n\log n\),不过反正我懒癌细胞发作懒得推了),就本题的数据范围,足矣。不过似乎还有神仙给出了线性的做法,这种做法需要用到线性递推的一些 trick,而我还没有深入学习线性递推,就先鸽着罢(((
这篇题解也是码死我了,题解长度堪比这篇题解
const int pr=3;
const int MAXN=5e4;
const int MAXP=1<<17;
const int MOD=998244353;
const int ipr=(MOD+1)/3;
int qpow(int x,int e=MOD-2){
int ret=1;
for(;e;e>>=1,x=1ll*x*x%MOD) if(e&1) ret=1ll*ret*x%MOD;
return ret;
}
int n,rev[MAXP+5];
int g[MAXN+5],G[MAXN+5][3],dp[MAXN+5][3];
void NTT(vector<int> &a,int len,int type){
int lg=31-__builtin_clz(len);
for(int i=0;i<len;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1));
for(int i=0;i<len;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
for(int i=2;i<=len;i<<=1){
int W=qpow((type<0)?ipr:pr,(MOD-1)/i);
for(int j=0;j<len;j+=i){
int w=1;
for(int k=0;k<(i>>1);k++,w=1ll*w*W%MOD){
int X=a[j+k],Y=1ll*w*a[(i>>1)+j+k]%MOD;
a[j+k]=(X+Y)%MOD;a[(i>>1)+j+k]=(X-Y+MOD)%MOD;
}
}
}
if(type==-1){
int ivn=qpow(len);
for(int i=0;i<len;i++) a[i]=1ll*a[i]*ivn%MOD;
}
}
vector<int> conv(vector<int> a,vector<int> b){
int len=1;while(len<a.size()+b.size()) len<<=1;
a.resize(len,0);b.resize(len,0);NTT(a,len,1);NTT(b,len,1);
for(int i=0;i<len;i++) a[i]=1ll*a[i]*b[i]%MOD;
NTT(a,len,-1);return a;
}
void cdq_FFT(int l,int r){
if(l==r){
dp[l][0]=(dp[l][0]+G[l][0])%MOD;
dp[l][1]=(dp[l][1]+G[l][1])%MOD;
dp[l][2]=(dp[l][2]+G[l][2])%MOD;
return;
} int mid=l+r>>1;cdq_FFT(l,mid);
vector<int> a,b,c;
for(int i=l;i<=mid;i++) a.pb(dp[i][0]);
for(int i=0;i<=r-l+1;i++) b.pb(G[i][0]);c=conv(a,b);
for(int i=mid+1;i<=r;i++) dp[i][0]=(dp[i][0]+c[i-l])%MOD;
a.clear();b.clear();
for(int i=l;i<=mid;i++) a.pb(dp[i][1]);
for(int i=0;i<=r-l+1;i++) b.pb(G[i][1]);c=conv(a,b);
for(int i=mid+1;i<=r;i++) dp[i][0]=(dp[i][0]+c[i-l])%MOD;
for(int i=mid+1;i<=r;i++) dp[i][2]=(dp[i][2]+c[i-l])%MOD;
a.clear();b.clear();
for(int i=l;i<=mid;i++) a.pb(dp[i][0]);
for(int i=0;i<=r-l+1;i++) b.pb(G[i][1]);c=conv(a,b);
for(int i=mid+1;i<=r;i++) dp[i][1]=(dp[i][1]+c[i-l])%MOD;
a.clear();b.clear();
for(int i=l;i<=mid;i++) a.pb(dp[i][1]);
for(int i=0;i<=r-l+1;i++) b.pb(G[i][2]);c=conv(a,b);
for(int i=mid+1;i<=r;i++) dp[i][1]=(dp[i][1]+c[i-l])%MOD;
a.clear();b.clear();
for(int i=l;i<=mid;i++) a.pb(dp[i][2]);
for(int i=0;i<=r-l+1;i++) b.pb(G[i][2]);c=conv(a,b);
for(int i=mid+1;i<=r;i++) dp[i][2]=(dp[i][2]+c[i-l])%MOD;
a.clear();b.clear();
cdq_FFT(mid+1,r);
}
int main(){
scanf("%d",&n);g[0]=g[2]=1;
for(int i=4;i<=n;i+=2) g[i]=(g[i-2]+g[i-4])%MOD;
for(int i=1;i<=n;i++){
G[i][0]=1ll*g[i-1]*(i-1)%MOD*(i-1)%MOD;
G[i][1]=1ll*g[i-2]*(i-1)%MOD*(i-1)%MOD;
G[i][2]=1ll*g[i-3]*(i-1)%MOD*(i-1)%MOD;
} cdq_FFT(1,n);int ans=1ll*n*(G[n][0]+G[n][2])%MOD;
for(int i=2;i<=n-2;i++){
ans=(ans+1ll*(1ll*G[i][0]*dp[n-i][0]+2ll*G[i][1]*dp[n-i][1]+
1ll*G[i][2]*dp[n-i][2])%MOD*i%MOD)%MOD;
} printf("%d\n",ans);
return 0;
}
Codeforces 848E - Days of Floral Colours(分治 FFT)的更多相关文章
- CodeForces 958F3 Lightsabers (hard) 启发式合并/分治 多项式 FFT
原文链接http://www.cnblogs.com/zhouzhendong/p/8835443.html 题目传送门 - CodeForces 958F3 题意 有$n$个球,球有$m$种颜色,分 ...
- CF848E Days of Floral Colours——DP+多项式求逆/分治NTT
官方题解:http://codeforces.com/blog/entry/54233 就是由简入繁 1.序列处理,只考虑一个半圆 2.环形处理(其实这个就是多了旋转同构) 然后基于分割线邻居的跨越与 ...
- [Codeforces 553E]Kyoya and Train(期望DP+Floyd+分治FFT)
[Codeforces 553E]Kyoya and Train(期望DP+Floyd+分治FFT) 题面 给出一个\(n\)个点\(m\)条边的有向图(可能有环),走每条边需要支付一个价格\(c_i ...
- CF960G Bandit Blues 第一类斯特林数+分治+FFT
题目传送门 https://codeforces.com/contest/960/problem/G 题解 首先整个排列的最大值一定是 \(A\) 个前缀最大值的最后一个,也是 \(B\) 个后缀最大 ...
- 再探快速傅里叶变换(FFT)学习笔记(其三)(循环卷积的Bluestein算法+分治FFT+FFT的优化+任意模数NTT)
再探快速傅里叶变换(FFT)学习笔记(其三)(循环卷积的Bluestein算法+分治FFT+FFT的优化+任意模数NTT) 目录 再探快速傅里叶变换(FFT)学习笔记(其三)(循环卷积的Blueste ...
- BNUOJ 51279[组队活动 Large](cdq分治+FFT)
传送门 大意:ACM校队一共有n名队员,从1到n标号,现在n名队员要组成若干支队伍,每支队伍至多有m名队员,求一共有多少种不同的组队方案.两个组队方案被视为不同的,当且仅当存在至少一名队员在两种方案中 ...
- hdu 5730 Shell Necklace [分治fft | 多项式求逆]
hdu 5730 Shell Necklace 题意:求递推式\(f_n = \sum_{i=1}^n a_i f_{n-i}\),模313 多么优秀的模板题 可以用分治fft,也可以多项式求逆 分治 ...
- BZOJ 4555: [Tjoi2016&Heoi2016]求和 [分治FFT 组合计数 | 多项式求逆]
4555: [Tjoi2016&Heoi2016]求和 题意:求\[ \sum_{i=0}^n \sum_{j=0}^i S(i,j)\cdot 2^j\cdot j! \\ S是第二类斯特林 ...
- 分治FFT的三种含义
分治FFT是几个算法的统称.它们之间并无关联. 分治多项式乘法 问题如求\(\prod_{i=1}^na_ix+b\). 若挨个乘复杂度为\(O(n^2\log n)\),可分治做这件事,复杂度为\( ...
随机推荐
- windows右键菜单自动打包发布nuget,没有CI/CD一样方便!
构建现代的 .Net 应用离不开 Nuget 的支持,而快速打包 Nuget 成了提高生产率的有效方法.没有CI/CD?来试试使用windows右键菜单吧 先看右键效果图 有时候我们可能没有CI/CD ...
- openmp学习心得(二)----常见的运行时库函数
omp_set_dynamic();如果设置了动态调整,并行区域会根据系统的资源状况,动态分配线程的数量.好像仅仅有0和非0的区别,设置为0不进行动态分配. omp_get_num_threads,o ...
- Less-5闯关失败
进行第五关的通关还是用之前的方式进行测试以及判断是什么类型的注入.通过判断我们不难发现是字符型注入.但是出了问题,我们会发现按照原来的步骤进行注入都会返回"You are in " ...
- ArrayList集合底层原理
目录 ArrayList集合特点及源码分析 ArrayList源码分析 成员变量 构造函数 增加方法 add(E e)方法 add(int index, E element)方法 删除方法 remov ...
- Ubuntu用apt安装MySQL
这里以MySQL5.7为例. # 如果之前有安装旧版,先移除sudo apt-get --purge remove mysql-server mysql-client mysql-common # 安 ...
- UVM RAL模型和内置seq
转载:UVM RAL模型:用法和应用_寄存器 (sohu.com) 在系统设计中通常会面临两大挑战:缩小技术节点的规模和上市时间(TTM,Time to Market).为了适应激烈的市场竞争,大多数 ...
- Qt信号与槽传递自定义数据类型——两种解决方法
信号与槽作为qt中的核心机制,在qt应用开发中经常会用的,但是原生的信号与槽连接传参,只支持基本的数据类型,比如char,int, float,double. 如果想要在信号与槽之间传递自定义参数,比 ...
- hdu 2795 Billboard(单点更新,区间查询)
题意: h*w的白板. 有n个广告牌,每个广告牌是1*wi.必须放置在白板的upmost中的leftmost. 输出n个广告牌放置在第几行.如果放不下,输出-1. 数据规格: h, w, and n ...
- 聊了聊宏内核和微内核,并吹了一波 Linux
看这里!!!https://mp.weixin.qq.com/s?__biz=MzI0ODk2NDIyMQ==&mid=2247494048&idx=1&sn=cacfc6a4 ...
- Spring一套全通—工厂
百知教育 - Spring系列课程 - 工厂 第一章 引言 1. EJB存在的问题 2. 什么是Spring Spring是一个轻量级的JavaEE解决方案,整合众多优秀的设计模式 轻量级 1. 对于 ...