SMSC 2021 Day13&Day14 部分题解
Day13
- 传播 spread (模拟)
真正的完美 inpref (数论,数论函数,线性筛)
P7788 [COCI2016-2017#6] Savrsen
考虑将
f
f
f 变为我们熟悉的函数,可以想到
∣
i
−
f
(
i
)
∣
=
∣
σ
1
(
i
)
−
2
i
∣
|i-f(i)| = |\sigma_1(i)-2i|
∣i−f(i)∣=∣σ1(i)−2i∣ ,其中
σ
1
(
n
)
=
∑
d
∣
n
d
\sigma_1(n) = \sum_{d|n}d
σ1(n)=∑d∣nd,是我们熟悉的约数和函数。它是一个积性函数,满足
a
⊥
b
⇔
f
(
a
⋅
b
)
=
f
(
a
)
⋅
f
(
b
)
a\bot b\Leftrightarrow f(a\cdot b) = f(a)\cdot f(b)
a⊥b⇔f(a⋅b)=f(a)⋅f(b)。所以考虑将
σ
1
\sigma_1
σ1 的计算加载到线性筛上。
在进行线性筛的过程中,除了筛选素数,在筛除合数的时候我们要计算它们的约数和。
下列式子显然:
σ
1
(
p
)
=
1
+
p
,
p
is a prime
σ
1
(
K
⋅
p
)
=
σ
1
(
K
)
×
σ
1
(
p
)
,
p
⊥
K
\begin{aligned} &\sigma_1(p) = 1 +p&,p\text{ is a prime}\\ &\sigma_1(K\cdot p) = \sigma_1(K)\times \sigma_1(p)&,p \bot K \end{aligned}
σ1(p)=1+pσ1(K⋅p)=σ1(K)×σ1(p),p is a prime,p⊥K
当
p
is a prime
,
K
m
o
d
p
=
0
p\text{ is a prime} ,K \bmod p =0
p is a prime,Kmodp=0 时,我们考虑将
K
⋅
p
K\cdot p
K⋅p 构造成两个互质的数的乘积,由于在线性筛中,枚举到的质数
p
p
p 是
K
⋅
p
K\cdot p
K⋅p 的最小质因子,所以我们考虑
K
=
∏
p
i
e
i
×
p
k
K
⋅
p
=
K
p
k
×
p
k
+
1
σ
1
(
K
⋅
p
)
=
σ
1
(
K
)
σ
1
(
p
k
)
×
σ
1
(
p
k
+
1
)
σ
1
(
p
k
+
1
)
=
∑
j
=
0
k
p
j
+
p
k
+
1
σ
1
(
K
⋅
p
)
=
σ
1
(
K
)
σ
1
(
p
k
)
×
(
σ
1
(
p
k
)
×
p
+
1
)
K = \prod p_i^{ei}\times p^k \\[2ex] K\cdot p=\dfrac{K}{p^k}\times p^{k+1}\\[2ex] \sigma_1(K\cdot p) = \dfrac{\sigma_1(K)}{\sigma_1(p^k)}\times \sigma_1(p^{k+1}) \\[2ex] \sigma_1(p^{k+1}) = \sum_{j = 0}^{k} p^j + p^{k+1} \\[2ex] \sigma_1(K\cdot p) = \dfrac{\sigma_1(K)}{\sigma_1(p^k)}\times(\sigma_1(p^k)\times p + 1)
K=∏piei×pkK⋅p=pkK×pk+1σ1(K⋅p)=σ1(pk)σ1(K)×σ1(pk+1)σ1(pk+1)=j=0∑kpj+pk+1σ1(K⋅p)=σ1(pk)σ1(K)×(σ1(pk)×p+1)
由此得解。
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
int n,sgm[10000005],cnt,p[10000005],pm[100000005],tmp;
bool NotPrime[10000005];
ll ans;
int myabs(int x)
{
return x > 0 ? x : (-x);
}
int qpow(int x,int y)
{
int res = 1;
for(;y;y >>= 1)
{
if(y%2) res = res*x;
x = x*x;
}
return res;
}
int main()
{
scanf("%d",&n);
NotPrime[1] = 1;sgm[1] = 1;
for(int i = 2;i <= n;i ++)
{
if(NotPrime[i] == 0)
{
cnt ++;
p[cnt] = i;
sgm[i] = i + 1;
pm[i] = 1;
}
for(int j = 1;j <= cnt && i*p[j] <= n;j ++)
{
NotPrime[i*p[j]] = 1;
if(i%p[j] == 0)
{
tmp = qpow(p[j],pm[i]);
sgm[i*p[j]] = sgm[i / tmp] * (sgm[tmp]*p[j] + 1);
pm[i*p[j]] = pm[i] + 1;
break;
}
sgm[i*p[j]] = sgm[i] * sgm[p[j]];
pm[i*p[j]] = 1;
}
}
for(int i = 1; i <= n;i ++)
{
ans += myabs(sgm[i] - i - i);
// cout << sgm[i] << endl;
}
printf("%lld",ans);
return 0;
}
两次 double (树状数组,可持久化线段树,莫队)
可以考虑树状数组离线回答询问。有一种巧妙的差分数组构造。考虑以下情况:
从当前的指针开始,第一个数字
i
i
i 不打标记,第二个数字
i
i
i 在树状数组对应位置中
+
1
+1
+1 ,第三个数字
i
i
i 在树状数组对应的位置中
−
1
-1
−1 ,这样当且仅当查询的区间内包含恰好两个
i
i
i 时,会对答案贡献
1
1
1 表示这个区间内恰有两个数字
i
i
i。
将询问按左端点降序排列后,从序列最右往左更新每个数的标记,更新到询问左端点时就回答询问,然后指针再向左移动,继续更新标记与回答询问,时间复杂度
O
(
n
)
O(n)
O(n)。注意要离散化一下。
#include<iostream>
#include<cstdio>
#include<map>
#include<algorithm>
using namespace std;
typedef long long ll;
int n,q;
int pre1[500005],pre2[500005],bit[500005],pre3[500005];
ll a[500005],num[500005];
struct quest{
int l;
int r;
int id;
int ans;
}b[1000005];
bool cmp2(quest a,quest b)
{
return a.id < b.id;
}
bool cmp1(quest a,quest b)
{
if(a.l != b.l) return a.l > b.l;
return a.r < b.r;
}
int lowbit(int x)
{
return x&(-x);
}
int BIT_Sum(int i)
{
int res = 0;
for(;i;i -= lowbit(i)) res += bit[i];
return res;
}
void BIT_Add(int i,int x)
{
for(;i <= n;i += lowbit(i)) bit[i] += x;
return ;
}
int main()
{
map<ll ,ll > m;
scanf("%d%d",&n,&q);
for(int i = 1;i <= n;i ++) {
scanf("%lld",&a[i]);
num[i] = a[i];
}
sort(num + 1,num + 1 + n);
for(int i = 1;i <= n;i ++) m[num[i]] = i;
for(int i = 1;i <= n;i ++) a[i] = m[a[i]];
for(int i = 1;i <= q;i ++)
{
scanf("%d%d",&b[i].l,&b[i].r);
b[i].id = i;
}
sort(b + 1,b + 1 + q,cmp1);
for(int i = 1,j = n;i <= q;i ++)
{
for(;j >= b[i].l;j --)
{
if(pre2[a[j]] == 0)
{
if(pre1[a[j]] == 0) pre1[a[j]] = j;
else
{
pre2[a[j]] = pre1[a[j]];
pre1[a[j]] = j;
BIT_Add(pre2[a[j]],1);
}
}
else
{
if(pre3[a[j]] > 0) BIT_Add(pre3[a[j]],1);
BIT_Add(pre2[a[j]],-2);
pre3[a[j]] = pre2[a[j]];
pre2[a[j]] = pre1[a[j]];
pre1[a[j]] = j;
BIT_Add(pre2[a[j]],1);
}
}
b[i].ans = BIT_Sum(b[i].r) - BIT_Sum(b[i].l - 1);
}
sort(b + 1,b + 1 + q,cmp2);
for(int i = 1;i <= q;i ++) printf("%d\n",b[i].ans);
return 0;
}
- 重新开始 rbegin (状压DP,Trie)
Day14
- 三角形 tri ()
- 缩写 gal ()
- 编辑距离 dis ()
- 红与黑 red ()