【数学思考】Codeforces Round #754 (Div. 2)E 待写
https://codeforces.com/contest/1605/problem/E
题意:对数组 进行若干次操作,变换为 数组。操作如下:
- 选择一个 ,对所有 的倍数 施以
- 选择一个 ,对所有 的倍数 施以
回答 次询问,每次询问改变 ,并求至少需要几次操作,才能将 变为 ?
题意翻译来源+某大佬的题解
我们可以考虑如果不改变b1那对于一个数列考虑
设置所有bi = bi-ai,ai = 0 发现不影响答案
然后我们最优的策略一定是先改变1然后2。。。
我们考虑发生了什么就可以O(n)处理
官方题解对于该方面考虑的就很好
然后我们发现对于每一个位置的改变都是一个数学式子,式子里包含若干bi*系数+系数*b1(未知的变量)
然后推一推就可以发现关于第一个位置的系数是莫比乌斯函数
所以我们将这样的系数加入数列。然后对于b1二分,(若b1前系数为+1我们将整个式子*-1由于求绝对值故不改变最后的数)。二分出来对于小于-b1的和大于-b1的分别考虑即完成对绝对值的处理。答案就出来了
语文不太好说得很抽象具体可以看官方题解,写得很好。
#include
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5;
int n;
int a[maxn], b[maxn];
ll qz[maxn], qzh[maxn], tot;
ll c[maxn], mob[maxn];
int pri[maxn], cnt;
bool nop[maxn];
void init() {
nop[1] = 1;
mob[1] = 1;
for (int i = 2; i <= n; i++) {
if (!nop[i]) {
pri[++cnt] = i;
mob[i] = -1;
}
for (int j = 1; j <= cnt && 1ll * i * pri[j] <= n; j++) {
nop[i * pri[j]] = 1;
if (i % pri[j] == 0)
break;
mob[i * pri[j]] = -mob[i];
}
}
}
ll sum(int l, int r) {
if (l > r)
return 0;
return qzh[r] - qzh[l - 1];
}
int main() {
scanf("%d", &n);
init();
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
for (int i = 1; i <= n; i++) {
scanf("%d", &b[i]);
}
for (int i = 1; i <= n; i++)
c[i] = b[i] - a[i];
c[1] = 0;
ll ans = 0;
for (int i = 1; i <= n; i++) {
for (int j = i + i; j <= n; j += i) {
c[j] -= c[i];
}
}
for (int i = 1; i <= n; i++) {
if (mob[i] == 0)
ans += llabs(c[i]);
else if (mob[i] == -1)
qz[++tot] = -c[i];
else
qz[++tot] = c[i];
}
sort(qz + 1, qz + 1 + tot);
for (int i = 1; i <= tot + 1; i++)
qzh[i] = qzh[i - 1] + qz[i];
int q;
scanf("%d", &q);
while (q--) {
int x;
scanf("%d", &x);
x -= a[1];
int wz = lower_bound(qz + 1, qz + 1 + tot, -x) - qz;
ll nans = sum(wz, tot) + 1ll * (tot - wz + 1) * x;
nans -= sum(1, wz - 1) + 1ll * (wz - 1) * x;
printf("%lld\n", ans + nans);
}
return 0;
}