title: 数论问题
tags: [数论]
categories: [OI, 算法专题]
P12237 蓝桥杯 2023 国 Java A 质数排序
原题 · 标签:#数论
考点
质数筛+计数技巧
题意
- 定义"质数排序":将序列中下标为质数的位置上的数按升序排序,其余位置不变。
- 对 \(1 \sim n\) 的所有排列分别进行质数排序,求所有结果序列的逆序对数量之和,对 \(998244353\) 取模。
数据范围:\(1 \le n \le 10^6\)。
思路
对质数位置进行升序排列后,质数位置之间不可能有逆序对。所以我们讨论质数位置与非质数位置间,非质数位置与非质数位置之间的答案。
设质数位置数量是 \(p\),非质数位置 \((n-p)\) 为 \(c\)。
由于是排列,所以对于在位置 \(i,j\) 填的两个数 \(a,b\),\((a,b)\) 和 \((b,a)\) 都会算到。所以这样一个数对的逆序对期望是 \(\frac{1}{2}\)。由此易算出非质数位置之间的答案数量为 \(\dfrac{n! \times c \times (c-1)}{4}\)。
非质数位置落在每个质数区间的概率是 \(\frac{1}{p+1}\),其前面质数数量与在质数中排位的绝对值就是其贡献。这个是两个等差数列的形式。
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int,int>
#define f(i,a,b) for(int i=(a);i<=(b);i++)
#define Dl(a) cout << #a << " : " << a << "\n";
#define D(a) cout << #a << " : " << a;
#define Da(a,i,j) cout << #a << " : ";f(idx,i,j){cout << a[idx] <<" ";}
const int MOD = 998244353;
const int N = 1e6+10;
int qpow(int x,int p){
int tmp = x;
int res = 1;
while (p){
if (p & 1) res = res * tmp % MOD;
tmp = tmp * tmp % MOD;
p /= 2;
}
return res;
}
bool notPrime[N];
vector <int> p;
void prime(int n){
notPrime[1] = 1;
for (int i=2;i<=n;i++){
if (!notPrime[i]) p.push_back(i);
for (int u:p){
if (u * i > n) break;
notPrime[u * i] = 1;
if (i % u == 0) break;
}
}
}
int n;
int inv2,inv4,invp1;
int fac[N];
void init(){
prime(n);
inv2 = qpow(2,MOD-2),inv4 = qpow(4,MOD-2),invp1 = qpow(p.size()+1,MOD-2);
fac[0] = 1;
for (int i=1;i<=n;i++){
fac[i] = fac[i-1] * i % MOD;
}
}
int ans;
void solve(){
int c = n - p.size();
// Dl(c);
ans += fac[n] * c % MOD * (c - 1) % MOD * inv4 % MOD;
int k = 0;
for (int i=1;i<=n;i++){
if (!notPrime[i]){
k++;
continue;
}
ans += fac[n] * invp1 % MOD * (k * (k + 1) % MOD * inv2 % MOD + (p.size() - k) * (p.size() - k + 1) % MOD * inv2 % MOD) % MOD;
ans %= MOD;
}
}
signed main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
cin >> n;
init();
solve();
cout << ans;
return 0;
}
ABC420G sqrt(n²+n+X)
考点
初中数学级别的推式子
思路
转化式子为:
\[\begin{aligned}
n^2 + n + x &= k^2 \\
n^2 + n - k^2 &= -x \\
\left(n + \frac{1}{2}\right)^2 - \frac{1}{4} - k^2 &= -x \\
(n + k + \tfrac{1}{2})(n - k + \tfrac{1}{2}) &= -x + \frac{1}{4} \\
(2n + 2k + 1)(2n - 2k + 1) &= -4x + 1
\end{aligned}
\]
对 \(-4x + 1\) 做分解,求出 \(n\),记得判断是不是解。
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int,int>
#define f(i,a,b) for(int i=(a);i<=(b);i++)
#define Dl(a) cout << #a << " : " << a << "\n";
#define D(a) cout << #a << " : " << a;
#define Da(a,i,j) cout << #a << " : ";f(idx,i,j){cout << a[idx] <<" ";}
int x;
set <int> ans;
void calc(int d1,int d2){
// if (d2 < d1) swap(d1,d2);
if (d1 % 4 == d2 % 4) return;
if ((d2 - d1 - 2) % 4 != 0) return;
int n = (d2 - d1 - 2) / 4;
__int128 check = __int128(n) * n + x + n;
if (check < 0) return;
ans.insert(n);
}
signed main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
cin >> x;
int y = 4 * x - 1;
for (int d1=1;d1*d1 <= llabs(y);d1++){
if (y % d1) continue;
int d2 = y / d1;
calc(d1,d2);
calc(d2,d1);
calc(d1 * -1,d2 * -1);
calc(d2*-1,d1*-1);
}
cout << ans.size() << "\n";
for (int u:ans){
cout << u << " ";
}
return 0;
}
浙公网安备 33010602011771号