#T1:
求
$ 1⩽A⩽B⩽2∗10^7$
#分析:
我们先将一开始的
i
=
A
i=A
i=A的情况下的所有数写下来,看成一个序列
即
−
A
/
1
+
A
/
2
−
…
…
+
(
−
)
A
/
A
-A/1+A/2-……+(-)A/A
−A/1+A/2−……+(−)A/A
然后我们发现,对于
A
+
1
A+1
A+1而言,
写下的序列与上一个不同的地方就是在
A
+
1
A+1
A+1的约数的位置,都比
A
A
A中序列多了或者少了一个这个
1
1
1,
对于
A
+
2
A+2
A+2,我们同理也能发现是在
A
+
1
A+1
A+1序列中,变化了
A
+
2
A+2
A+2的约数
那么我们就可以考虑,
先将
A
A
A的序列处理出来,然后总和设为
x
x
x,那么则答案必有
(
B
−
A
+
1
)
∗
x
(B-A+1)*x
(B−A+1)∗x,
然后就是要处理所有数
i
i
i到数
i
+
1
i+1
i+1对答案的贡献,
则我们可以枚举约数
z
z
z,
则我们考虑
z
z
z对答案的贡献,
显然
[
A
.
.
B
]
[A..B]
[A..B]中所有数都有
A
/
z
A/z
A/z
然后设
C
C
C为大于
A
A
A中最小的
z
z
z的倍数,
可以发现
[
C
.
.
B
]
[C..B]
[C..B]中所有的数写成序列,在
z
z
z这个位置,则都有
A
/
z
+
1
A/z+1
A/z+1,
然后
[
C
+
z
.
.
B
]
[C+z..B]
[C+z..B]中所有的数序列,在
z
z
z这个位置,则都有
A
/
z
+
2
A/z+2
A/z+2,
后面同理,
那么我们可以发现这样的话,对于约数
z
z
z在
[
A
.
.
B
]
[A..B]
[A..B]中答案的贡献,可以通过推理O(1)算出,然后这个贡献,因为已经全部减去了
(
B
−
A
+
1
)
∗
(
A
/
z
)
(B-A+1)*(A/z)
(B−A+1)∗(A/z),
则
z
z
z对答案还存在的贡献为
1
+
2
+
3
+
…
…
+
[
A
+
1..
B
]
中
所
有
的
z
的
倍
数
的
个
数
1+2+3+……+[A+1..B]中所有的z的倍数的个数
1+2+3+……+[A+1..B]中所有的z的倍数的个数
这个可以通过等差数列O(1)算出
一开始用A造了个序列,
时
间
复
杂
度
O
(
A
)
时间复杂度O(A)
时间复杂度O(A)
最后枚举了约数,
时
间
复
杂
度
O
(
B
)
时间复杂度O(B)
时间复杂度O(B)
时
间
复
杂
度
:
O
(
A
+
B
)
时间复杂度:O(A+B)
时间复杂度:O(A+B)
#代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;
int A, B;
int main()
{
scanf("%d %d", &A, &B);
ll ans = 0;
for (int i = 1; i <= A; i++)
ans = ans + (A / i) * ((i % 2) ? -1 : 1);
ans *= (B - A + 1);
for (int i = 1; i <= B; i++)
{
ll C = A / i * i;
if (C <= A) C += i;
if (C > B) continue;
ll D = (B - C + 1);
ll E = (B - C) / i;
D = D + D * E;
E = (double)((1 + E) * E / 2);
E *= i;
ans += ((D - E) * ((i % 2) ? -1 : 1));
}
printf("%lld", ans);
return 0;
}
#T2:
#题目大意:
#分析:
对于任意两条直线
k
1
x
+
b
1
k_1x+b_1
k1x+b1,
k
2
x
+
b
2
k_2x+b_2
k2x+b2,如果他们相交,即
k
k
k不相同,
则他们的交点显然为
(
b
2
−
b
1
k
1
−
k
2
,
k
1
b
2
−
b
1
k
1
−
k
2
+
b
1
)
(\frac{b_2-b_1}{k_1-k_2},k_1\frac{b_2-b_1}{k_1-k_2}+b_1)
(k1−k2b2−b1,k1k1−k2b2−b1+b1)
我们离线操作排序
X
=
A
j
X=A_j
X=Aj,即保证
X
=
A
j
X=A_j
X=Aj的递增,
先行求出
X
=
m
i
n
(
A
j
)
X=min(A_j)
X=min(Aj)时,所有直线与它的相交的点的大小情况,然后求出一开始的顺序,
设
A
j
A_j
Aj升序排列后的数为
B
1
,
B
2
.
.
.
B
M
B_1,B_2...B_M
B1,B2...BM
如果我们求出了
X
=
B
i
X=B_i
X=Bi时相交点的顺序,那么此时这些点在
X
=
B
i
到
X
=
B
i
+
1
X=B_i到X=B_{i+1}
X=Bi到X=Bi+1的区间里,如果存在有点使得任意2条直线相交,则这两条直线的大小关系就会分别加减1。
所以我们线处理出两两直线间的交点然后离线即可,注意细节。
时间复杂度最坏是
O
(
M
+
N
2
)
O(M+N^2)
O(M+N2)
#代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define M 500005
#define N 2005
using namespace std;
typedef long double ld;
typedef long long ll;
struct Node { ld x; int CF, DF; }A[N*N];
struct Text { ld ask; int id; }C[M];
struct Code { ld y; int K, B; }G[N];
int Rank[N], Now[N], cnt, n, m, k;
ll Ans[M];
bool cmp(Text aa, Text bb)
{
return aa.ask < bb.ask;
}
bool cmp1(Code aa, Code bb)
{
return aa.y < bb.y;
}
bool cmp2(Node aa, Node bb)
{
return aa.x < bb.x;
}
int main()
{
scanf("%d %d %d", &n, &m, &k);
for (int i = 1; i <= n; i++)
scanf("%d %d", &G[i].K, &G[i].B);
for (int i = 1; i <= m; i++)
{
scanf("%llf", &C[i].ask);
C[i].id = i;
}
sort(C + 1, C + m + 1, cmp);
for (int i = 1; i <= n; i++)
G[i].y = G[i].K * C[1].ask + G[i].B, Rank[i] = i, Now[i] = i;
sort(G + 1, G + n + 1, cmp1);
Ans[C[1].id] = G[k].y;
for (int i = 2; i <= n; i++)
for (int j = i - 1; j >= 1; j--)
if (G[i].K != G[j].K)
{
ld X_C = (G[j].B - G[i].B) / (G[i].K - G[j].K);
if (X_C > C[1].ask)
A[++cnt].x = X_C, A[cnt].CF = i, A[cnt].DF = j;
}
sort(A + 1, A + cnt + 1, cmp2);
int l = 0;
for (int i = 2; i <= m; i++)
{
while (A[l + 1].x <= C[i].ask && l < cnt)
{
l++;
Now[--Rank[A[l].CF]] = A[l].CF;
Now[++Rank[A[l].DF]] = A[l].DF;
}
Ans[C[i].id] = G[Now[k]].K * C[i].ask + G[Now[k]].B;
}
for (int i = 1; i <= m; i++) printf("%lld\n", Ans[i]);
return 0;
}