感谢wls的camp,么么哒。
昨天下午比赛结束前赶到机房,爆写300行树剖线段树之后没时间了,晚上带着一票人出去烧烤,现在牙龈还肿痛着,当然这都不是问题。
今天来到了阶梯教室,无空调,人太多了,还有异味…不过都是小事>v<。
回到今天的重点,今天的知识点是数论。
NWERC2015 Debugging
http://codeforces.com/gym/101485/attachments
sol1
s
o
l
1
. 单纯的logn次二分运行的时间下界是
logn∗(r+p)
l
o
g
n
∗
(
r
+
p
)
,每次程序分为2部分,而我们有更优秀的做法。
sol2.
s
o
l
2.
f(n)代表n行代码运行的最少时间,f是个不降序列,每次枚举程序划分几部分,显然这部分答案会包含二分的答案。
考虑x行printf均分n行代码,规模缩小,向下记忆化搜索就即可。
需要注意向上取整的分块方法的写法。
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn = 1e6+10;
LL len, r, p;
LL dp[maxn];
LL cal(LL n) {
if (n <= 1) return 0;
if (~dp[n]) return dp[n];
LL ans = (n - 1) * p + r;
int last;
for (int i = 2; i < n; i = last + 1) {
last = (n - 1) / ((n - 1) / i);
ans = min(ans, (i - 1) * p + cal((n + i- 1) / i) + r);
}
return dp[n] = ans;
}
int main() {
cin >> len >> r >> p;
memset(dp, -1, sizeof dp);
cout << cal(len);
return 0;
}
15长春B
http://acm.hdu.edu.cn/showproblem.php?pid=5528
首先
a
a
和都是
[0,n)
[
0
,
n
)
的数,为了方便计数,我们把区间转化为
[1,n]
[
1
,
n
]
,转化的正确性在于我们想要计算
ab|n
a
b
|
n
的对数,在这个意义下,
0
0
和代表的含义相同。
f(n)
f
(
n
)
代表
a∗b
a
∗
b
不能被
n
n
整除的方案数
令为
a∗b
a
∗
b
能被
n
n
整除的方案数
那么
又有
g(n)=∑d|nf(d)=∑d|nd2−∑d|nh(d)
g
(
n
)
=
∑
d
|
n
f
(
d
)
=
∑
d
|
n
d
2
−
∑
d
|
n
h
(
d
)
可以发现左侧和右侧分别都是积性函数,因此可以拆分成
n=pk11∗pk22....pkxx
n
=
p
1
k
1
∗
p
2
k
2
.
.
.
.
p
x
k
x
的形式,而对于每一部分有
ki+1
k
i
+
1
个因子可以计数,因此复杂度的瓶颈就变成了因式分解。
值得一提的是:
∑d|nϕ(d)=n=>I∗ϕ=n,I
∑
d
|
n
ϕ
(
d
)
=
n
=>
I
∗
ϕ
=
n
,
I
为常值函数。
对于
∑d|nh(d)=∑d|n∑w|dϕ(dw)w
∑
d
|
n
h
(
d
)
=
∑
d
|
n
∑
w
|
d
ϕ
(
d
w
)
w
记
id(x)=x
i
d
(
x
)
=
x
, 那么
∑d|n∑w|dϕ(dw)w=I∗ϕ∗id
∑
d
|
n
∑
w
|
d
ϕ
(
d
w
)
w
=
I
∗
ϕ
∗
i
d
狄利克雷卷积符合交换律和结合律,因此
I∗ϕ∗id=(I∗ϕ)∗id=∑d|n∑w|dϕ(w)=id∗id=∑d|ndnd=n∗facnum(n)
I
∗
ϕ
∗
i
d
=
(
I
∗
ϕ
)
∗
i
d
=
∑
d
|
n
∑
w
|
d
ϕ
(
w
)
=
i
d
∗
i
d
=
∑
d
|
n
d
n
d
=
n
∗
f
a
c
n
u
m
(
n
)
此处的分解因式复杂度为
n−−√/logn
n
/
l
o
g
n
。
#include <bits/stdc++.h>
#define LL long long
#define ULL unsigned long long
using namespace std;
int t;
int n;
bitset<100000> bit;
vector<int> p;
void shai() {
const int block = sqrt(1e9+10);
for (int i = 2; i <= block; i++) {
if (bit[i] == 0) {
p.push_back(i);
for (int j = i+i; j <= block; j += i){
bit[j] = 1;
}
}
}
}
int main() {
shai();
cin.tie(0);
ios::sync_with_stdio(time(0));
cin >> t;
while (t--) {
cin >> n;
vector<pair<int, int>> pr;
for (auto i:p) {
if (i > n) break;
if (n % i == 0) {
int val = i;
int cnt = 0;
while (n % i == 0) {
cnt++;
n /= i;
}
pr.push_back({val, cnt});
}
}
if (n != 1) {
pr.push_back({n, 1});
}
ULL res1 = 1;
ULL res2 = 1;
for (int i = 0; i < pr.size(); i++) {
ULL ans1 = 0;
ULL ans2 = 0;
int val = pr[i].first, cnt = pr[i].second;
LL tmp = 1;
for (int j = 0; j <= cnt; j++) {
ans1 += 1ULL*tmp*tmp;
tmp *= val;
}
ans2 += (cnt+1)*(tmp/val);
res1 *= ans1;
res2 *= ans2;
}
cout << res1 - res2 << '\n';
}
return 0;
}
codeforces757E
对于
gcd(a,b)=1,a∗b=n,
g
c
d
(
a
,
b
)
=
1
,
a
∗
b
=
n
,
考虑n的不同质因子个数
k
k
,那么方案数就是
2k
2
k
。
将
n
n
拆成,且
f
f
是个积性函数,因此。
f[0][pkii]=2
f
[
0
]
[
p
i
k
i
]
=
2
f[r][pkii]=∑d|pkiif[r−1][d]
f
[
r
]
[
p
i
k
i
]
=
∑
d
|
p
i
k
i
f
[
r
−
1
]
[
d
]
因此预处理
f
f
数组,单词询问的复杂度就变成了分解因式的复杂度。
#define others
#ifdef poj
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <set>
#endif // poj
#ifdef others
#include <bits/stdc++.h>
#include <ext/rope>
#include <ext/pb_ds/priority_queue.hpp>
#endif // others
//#define file
#define all(x) x.begin(), x.end()
using namespace std;
using namespace __gnu_pbds;
using namespace __gnu_cxx;
#define eps 1e-8
const double pi = acos(-1.0);
typedef long long LL;
typedef long long ll;
typedef unsigned long long ULL;
void umax(int &a, int b)
{
a = max(a, b);
}
void umin(int &a, int b)
{
a = min(a, b);
}
int dcmp(double x)
{
return fabs(x) <= eps?0:(x > 0?1:-1);
}
void file()
{
freopen("data_in.txt", "r", stdin);
freopen("data_out.txt", "w", stdout);
}
const LL mod = 1e9+7;
ll powmod(ll a,ll b)
{
ll res=1;
a%=mod;
assert(b>=0);
for(; b; b>>=1)
{
if(b&1)res=res*a%mod;
a=a*a%mod;
}
return res;
}
//#define iostart
#define pb(x) push_back(x)
namespace solver {
LL f[1100000][20];
const LL mod = 1e9+7;
int tag[1100];
vector<int> G;
void init() {
for (int i = 2; i < 1100; i++) {
if (tag[i] == 0) {
G.push_back(i);
for (int j = i + i; j < 1100; j+=i) {
tag[j] = 1;
}
}
}
f[0][0] = 1;
for (int i = 1; i < 20; i++) {
f[0][i] = 2;
}
for (int i = 1; i < 1100000; i++){
f[i][0] = 1;
for (int j = 1; j < 20; j++) {
f[i][j] = f[i-1][j] + f[i][j-1];
if (f[i][j] >= mod) f[i][j] %= mod;
}
}
}
void solve() {
init();
int q;
scanf("%d", &q);
while (q--) {
int n, r;
scanf("%d%d", &r, &n);
LL ans = 1;
for (int i = 0; i < G.size(); i++) {
if (G[i] > n) {
break;
}
int val = G[i], cnt = 0;
if (n % val == 0) {
while (n % val == 0) {
cnt ++;
n /= val;
}
ans *= f[r][cnt];
ans %= mod;
}
}
if (n != 1) {
ans *= f[r][1];
ans %= mod;
}
cout << ans << '\n';
}
}
}
int main() {
#ifdef iostart
ios::sync_with_stdio(0);
cin.tie(0);
#endif // iostart
// file();
solver::solve();
return 0;
}
*/
随后讲解线性筛,引出积性函数前缀和。
1. 如果f(p)可以logn求出来,那么O(n)OK
2.
低于线性时间对数论函数求和
四川省赛GRISAIA
https://www.oj.swust.edu.cn/problem/show/2810
让我好找啊…> <
#define others
#ifdef poj
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <set>
#endif // poj
#ifdef others
#include <bits/stdc++.h>
#include <ext/rope>
#include <ext/pb_ds/priority_queue.hpp>
#endif // others
//#define file
#define all(x) x.begin(), x.end()
using namespace std;
using namespace __gnu_pbds;
using namespace __gnu_cxx;
#define eps 1e-8
const double pi = acos(-1.0);
typedef long long LL;
typedef __int128 DLL;
typedef unsigned long long ULL;
void umax(LL &a, LL b) {
a = max(a, b);
}
void umin(LL &a, LL b) {
a = min(a, b);
}
int dcmp(double x) {
return fabs(x) <= eps?0:(x > 0?1:-1);
}
void file() {
freopen("data_in.txt", "r", stdin);
freopen("data_out.txt", "w", stdout);
}
const LL mod = 1e9+7;
LL Pow(LL a,LL b) {
LL res=1;
a%=mod;
for(; b; b>>=1) {
if(b&1)res=res*a%mod;
a=a*a%mod;
}
return res;
}
void print(DLL x) {
if(x < 0) {
x = -x;
putchar('-');
}
if(x > 9) print(x/10);
putchar(x%10 + '0');
}
//#define iostart
#define pb(x) push_back(x)
namespace solver {
int t;
LL n;
DLL g[30000000];
const int maxn = 30000000;
bool tag[maxn];
int p[maxn/10];
int cnt;
void GetPrime(){
cnt = 0;
g[1] = 1;
for(int i = 2; i < maxn; i++){
if(!tag[i]) {
p[cnt++] = i;
g[i] = (1 + i);
}
for(int j = 0; j < cnt && p[j] * i < maxn; j++){
tag[i * p[j]] = 1;
if(i % p[j] == 0) {
int cnt = 0, xx = i;
LL v = 1;
while (xx%p[j]==0) {
cnt ++;
v *= p[j];
xx/=p[j];
}
v *= p[j];
g[i*p[j]] = g[i]*(v*p[j]-1)/(v-1);
break;
}
g[i*p[j]] = g[i]*(1+p[j]);
}
}
for (int i = 1; i < maxn; i++) {
g[i] += g[i-1];
}
}
DLL f(LL v) {
if (v < maxn) return g[v];
DLL res = 0;
for (DLL i = 1, last; i <= v; i = last + 1) {
DLL xx = v / i;
last = v / xx;
res += (i + last) * (last - i + 1) / 2 * xx;
}
return res;
}
void solve() {
GetPrime();
scanf("%d", &t);
while (t--) {
scanf("%lld", &n);
DLL x = n;
x = (x * x * (1 + x)) / 2;
DLL z = 0;
for (DLL i = 1; i * i<= n; i++) {
z += i * i * (n / (i * i));
}
DLL y = 0;
for (DLL i = 1, last; i <= n; i = last + 1) {
DLL xx = n / i;
last = n / xx;
y += (last + i) * (last - i + 1) / 2 * f(xx);
}
y += z;
x -= y / 2;
print(x);
cout << '\n';
}
}
}
int main() {
#ifdef iostart
ios::sync_with_stdio(0);
cin.tie(0);
#endif // iostart
// file();
solver::solve();
return 0;
}
BZOJ2820
枚举质数,转化成三个求和的,ij互质的个数。
#define others
#ifdef poj
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <set>
#endif // poj
#ifdef others
#include <bits/stdc++.h>
#include <ext/rope>
#include <ext/pb_ds/priority_queue.hpp>
#endif // others
//#define file
#define all(x) x.begin(), x.end()
using namespace std;
using namespace __gnu_pbds;
using namespace __gnu_cxx;
#define eps 1e-8
const double pi = acos(-1.0);
typedef long long LL;
typedef int DLL;
typedef unsigned long long ULL;
void umax(LL &a, LL b) {
a = max(a, b);
}
void umin(LL &a, LL b) {
a = min(a, b);
}
int dcmp(double x) {
return fabs(x) <= eps?0:(x > 0?1:-1);
}
void file() {
freopen("data_in.txt", "r", stdin);
freopen("data_out.txt", "w", stdout);
}
const LL mod = 998244353;
LL Pow(LL a,LL b) {
LL res=1;
a%=mod;
for(; b; b>>=1) {
if(b&1)res=res*a%mod;
a=a*a%mod;
}
return res;
}
//
//void print(DLL x) {
// if(x < 0) {
// x = -x;
// putchar('-');
// }
// if(x > 9) print(x/10);
// putchar(x%10 + '0');
//}
//#define iostart
#define pb(x) push_back(x)
namespace solver {
int n;
const int maxn = 1e7 + 10;
bool tag[maxn];
int cnt = 0;
int p[maxn/10];
LL mob[maxn], g[maxn];
void shai() {
cnt = 0;
mob[1] = 1;
for (int i = 2; i < maxn; i++) {
if (!tag[i]) {
p[cnt++] = i;
mob[i] = -1;
}
for (int j = 0; j < cnt && p[j] * i < maxn; j++){
tag[i*p[j]] = 1;
if (i % p[j] == 0) {
mob[i*p[j]] = 0;
break;
}
mob[i*p[j]] = -mob[i];
}
}
for (int i = 0; i < cnt; i++) {
for (int j = p[i]; j < maxn; j += p[i]) {
g[j] += mob[j/p[i]];
}
}
for (int i = 1; i < maxn; i++) {
// cout << i << " " << mob[i] << endl;
// getchar();
g[i] += g[i-1];
}
}
void solve() {
shai();
int t;
scanf("%d", &t);
while (t--) {
int n, m;
scanf("%d%d", &n, &m);
LL ans = 0;
int x = min(n, m);
for (int i = 1, last; i <= x; i = last + 1) {
last = min(n/(n/i), m/(m/i));
ans += 1LL*(n/i)*(m/i)*(g[last] - g[i-1]);
}
cout << ans << '\n';
}
}
}
int main() {
#ifdef iostart
ios::sync_with_stdio(0);
cin.tie(0);
#endif // iostart
// file();
solver::solve();
return 0;
}
杜教筛
低于线性时间积性函数前缀和