• bzoj 3853: GCD Array


    题目传送门:bzoj 3853

    题意简述:

    有一个数组 (a),下标范围为 (1 sim l),初始都是 (0)

    (Q) 次操作,为以下两类之一:

    1. 1 n d v:对于所有满足 (gcd(i, n) = d) 的下标 (i),令 (a_i) 加上 (v)
    2. 2 x:求 (displaystyle sum_{i = 1}^{x} a_i)

    数据范围:(Q le 5 cdot {10}^4)(n le 2 cdot {10}^5),运算在 long long 范围内。

    题解:

    操作 1,如果 (d) 不是 (n) 的因数,就没用。

    否则把 (n) 除掉 (d),则可以变换为:对于所有满足 (gcd(i, n) = 1)(i),令 (a_{i cdot d}) 加上 (v)

    也就是说,每个 (a_{i cdot d}),要加上 (displaystyle v sum_{egin{subarray}{c} k | i \ k | n end{subarray}} mu(k))

    那么我们先把条件 (k | n) 提前,先枚举 (k),变成:

    枚举 (k | n),对于所有 (kd) 的倍数 (i),令 (a_i) 加上 (v mu(k))

    我们发现,这是:每次操作令下标为某个数的倍数的位置加上 (v)

    那么我们不维护原数组了,直接维护一个 (c) 数组,如果 (c_i = x),表示原数组中下标为 (i) 的倍数的位置都加上了 (x)

    则可以得到:(displaystyle a_i = sum_{j | i} c_j)

    那么询问的时候就是查询 (displaystyle sum_{i = 1}^{x} sum_{j | i} c_j)

    交换求和顺序:(displaystyle sum_{j = 1}^{x} c_j !leftlfloor frac{x}{j} ight floor)

    (displaystyle leftlfloor frac{x}{j} ight floor) 进行整除分块,只要维护 (c) 的区间和,用树状数组维护即可。

    代码如下,时间复杂度为 (mathcal O (Q sqrt{n} log l))

    #include <cstdio>
    
    typedef long long LL;
    const int MN = 200005, MP = 17985;
    const int ML = 50005;
    
    bool ip[MN];
    int p[MP], pc;
    int mu[MN];
    int h[MN], nxt[1641422], to[1641422], tot;
    inline void ins(int x, int y) { nxt[++tot] = h[x], to[tot] = y, h[x] = tot; }
    inline void Sieve(int N) {
    	mu[1] = 1;
    	for (int i = 2; i <= N; ++i) {
    		if (!ip[i]) p[++pc] = i, mu[i] = -1;
    		for (int j = 1, k; j <= pc; ++j) {
    			if ((k = p[j] * i) > N) break;
    			ip[k] = 1;
    			if (i % p[j]) mu[k] = -mu[i];
    			else break;
    		}
    	}
    	for (int i = 1; i <= N; ++i) if (mu[i])
    		for (int j = i; j <= N; j += i) ins(j, i);
    }
    
    int Len, Q;
    LL bit[ML];
    inline void Add(int i, int x) { for (; i <= Len; i += i & -i) bit[i] += x; }
    inline LL Qur(int i) { LL a = 0; for (; i; i -= i & -i) a += bit[i]; return a; }
    
    int main() {
    	Sieve(200000);
    	int T = 0;
    	while (~scanf("%d%d", &Len, &Q) && Len && Q) {
    		for (int i = 1; i <= Len; ++i) bit[i] = 0;
    		printf("Case #%d:
    ", ++T);
    		while (Q--) {
    			int opt;
    			scanf("%d", &opt);
    			if (opt == 1) {
    				int n, d, v;
    				scanf("%d%d%d", &n, &d, &v);
    				if (n % d) continue;
    				n /= d;
    				for (int id = h[n]; id; id = nxt[id]) {
    					int k = to[id];
    					if (k * d <= Len) Add(k * d, v * mu[k]);
    				}
    			} else {
    				int x;
    				scanf("%d", &x);
    				LL Ans = 0;
    				for (int i = 1, j, k; i <= x; i = j + 1) {
    					k = x / i, j = x / k;
    					Ans += (LL)k * (Qur(j) - Qur(i - 1));
    				}
    				printf("%lld
    ", Ans);
    			}
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    django 模型ManyToMany 关联的添加,删除,查询
    js 购物车的数量加减,对应的总价也随机变化
    `Java`中`abstract class`与`interface`区别
    基于Netty的IdleStateHandler实现Mqtt心跳
    由MQTT topic的正则表达式匹配引发的特殊字符"/"匹配思考
    Maven项目配置Logback输出JSON格式日志
    MySql定时备份脚本
    使用tcpdump监控http流量
    Linux磁盘信息查询及删除文件操作
    基于Morphia实现MongoDB按小时、按天聚合操作
  • 原文地址:https://www.cnblogs.com/PinkRabbit/p/12274365.html
Copyright © 2020-2023  润新知