[航海协会]摆

Posted StaroForgin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[航海协会]摆相关的知识,希望对你有一定的参考价值。

题目描述


题解

首先,我们观察一下这个矩阵,看它有什么性质,你会发现它是长这个样子的:

一个上三角的部分全部都是 C C C,中线是 1 1 1,下面有的是 C C C有的是 0 0 0
由于矩阵上把某一行加或减在另一行上,并不会改变该矩阵行列式的值,我们不妨将每一行都减去它下一行的值,于是你会发现它变成这个样子了:

画的好丑呀
也就是一个上海森堡矩阵,右上是一个全 0 0 0的三角,左下角 i i i的倍数处为 C C C i i i的倍数减一处为 − C -C C
首先对于这种海森堡矩阵,我们的行列式可以考虑从行列式的定义入手求解。
定义的方法相当于是我们枚举一个排列,计算排列的乘积。
由于排列中肯定是存在置换环的,不妨考虑单个置换环会怎么产生贡献。
显然,在这个环中肯定存在一个 i i i使得 p i > i p_i>i pi>i,而走到 p i p_i pi后,我们的 p p i p_p_i ppi肯定是 p i − 1 p_i-1 pi1了。
因为我们的上边只存在 ( i − 1 , i ) (i-1,i) (i1,i)的点,我们往回走就只能从这个点走,再往后走就回不来了,而且往回走还每次只能走一步。
我们考虑这个环会产生怎样的贡献,走回来时是每步贡献 C − 1 C-1 C1,走过去时每步贡献 C C C,并且每个环还会贡献一个逆序对。
看起来太麻烦了,我们干脆给每个点都除去一个 C − 1 C-1 C1,这样就相当于我们一个环贡献就直接乘上一个 C 1 − C \\fracC1-C 1CC
我们定义 f i f_i fi表示大小为 i × i i\\times i i×i的上海森堡矩阵的行列式,容易得到转移方程:
f i = f i − 1 + C 1 − C ∑ d ∣ i ∧ d ≠ i f d − f d − 1 f_i=f_i-1+\\fracC1-C\\sum_d|i\\wedge d\\neq if_d-f_d-1 fi=fi1+1CCdid=ifdfd1 f i − 1 f_i-1 fi1减到左边去,就变成了一个差分的形式,记差分的为 g i g_i gi,有转移式:
g i = ∑ d ∣ i ∧ d ≠ i C 1 − C g d g_i=\\sum_d|i\\wedge d\\neq i\\fracC1-Cg_d gi=did=i1CCgd显然, g g g的前缀和可以通过杜教筛求解,我们考虑将 C 1 − C I \\fracC1-CI 1CCI g g g卷在一起,就成了经典的杜教筛形式。
我们可以先预处理出来前 n 2 3 n^\\frac23 n32处的 g i g_i gi值,后面的部分就可以 O ( n 2 3 ) O\\left(n^\\frac23\\right) O(n32)的数论分块快速计算。
问题就是前面的 g g g值也是不能暴力计算的,否则会 T T T飞。
可以考虑对于 m = ∏ p i a i m=\\prod p_i^a_i m=piai g m g_m gm的值显然只与集合 A A A有关。
所以我们不妨考虑对于每种集合,选一个数暴力 O ( m ) O\\left(\\sqrtm\\right) O(m )计算。
显然,这样的集合个数是 O ( P ( log ⁡ m ) ) O(P(\\log m)) O(P(logm))级别的,这部分的计算时间复杂度比较小。
当然,对于集合的存储我们可以考虑 h a s h hash hash,这样就能相当快地找到它属于哪个集合了。
加上 H a s h M a p HashMap HashMap这部分就能线性了。
不过需要注意的是我们算出来 f n f_n fn并不一定是答案,还要乘上我们之前除去的 ( 1 − C ) n − 1 (1-C)^n-1 (1C)n1,其中第一行没除,所以是 n − 1 n-1 n1次方,我们的答案实际上是 ( 1 − C ) n − 1 f n (1-C)^n-1f_n (1C)n1fn

总时间复杂度 O ( n 2 3 ) O\\left(n^\\frac23\\right) O(n32)

源码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
typedef unsigned int uint;
#define MAXN 20000005
#define MAXM 1000010
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
const LL INF=0x3f3f3f3f3f3f3f3f;
const int mo=998244353;
const int mod=1e6+7;
const int lim=20000000;
template<typename _T>
void read(_T &x)
   _T f=1;x=0;char s=getchar();
   while(s<'0'||s>'9')if(s=='-')f=-1;s=getchar();
   while('0'<=s&&s<='9')x=(x<<3)+(x<<1)+(s^48);s=getchar();
   x*=f;

template<typename _T>
_T Fabs(_T x)return x<0?-x:x;[航海协会]稀疏阶乘问题

[航海协会]基因切割

[航海协会]万灵药

[航海协会]SSSP

[航海协会]身体

[航海协会]身体