• 【ybtoj高效进阶 21290】头文件 D(平衡规划)(线段树)


    头文件 D

    题目链接:ybtoj高效进阶 21290

    题目大意

    给你一个序列,下标从 1 到 n。
    然后有两类操作,要么是给出 k,l,r,x 把所有下标 %k 的值在 l~r 之间的位置都加上 x。
    要么是区间求和。

    思路

    发现没有什么比较好的数据结构能实现。

    考虑观察它修改的性质。
    如果它 (k) 很大,那修改的段就很小,可以直接暴力枚举每个段。
    如果它 (k) 很小,那它修改段很多,但是每段很小,可以暴力用前缀和的方法统计。

    所以我们考虑使用平衡规划。
    自然分成 (kleqslantsqrt{n})(k>sqrt{n}) 两个部分。
    第一个部分,我们直接 (f_{i,j})(k=i) 的修改,它第 (j) 个位置是加了 (f_{i,j})
    然后你在每次搞了就重新计算前缀和 (g_{i,j})
    然后就可以很快的统计了。

    第二个部分,直接暴力找每一段线段树赋值,然后插叙就直接查询线段树即可。

    代码

    #include<cmath>
    #include<cstdio>
    #include<iostream>
    #define ll long long
    
    using namespace std;
    
    ll n, m, op, x, y, k, z;
    ll f[501][501], g[501][501];
    ll a[200001], ans, sum[501];
    
    struct XD_tree {//线段树
    	ll v[200002 << 2], lzy[200002 << 2];
    	
    	void up(ll now) {
    		v[now] = v[now << 1] + v[now << 1 | 1];
    	}
    	
    	void down(ll now, ll l, ll r) {
    		if (!lzy[now]) return ;
    		ll mid = (l + r) >> 1;
    		v[now << 1] += lzy[now] * (mid - l + 1); v[now << 1 | 1] += lzy[now] * (r - mid);
    		lzy[now << 1] += lzy[now]; lzy[now << 1 | 1] += lzy[now]; 
    		lzy[now] = 0;
    	}
    	
    	void build(ll now, ll l, ll r) {
    		if (l == r) {
    			v[now] = a[l]; return ;
    		}
    		ll mid = (l + r) >> 1;
    		build(now << 1, l, mid);
    		build(now << 1 | 1, mid + 1, r);
    		up(now);
    	}
    	
    	void insert(ll now, ll l, ll r, ll L, ll R, ll va) {
    		if (L <= l && r <= R) {
    			lzy[now] += va; v[now] += 1ll * va * (r - l + 1);
    			return ;
    		}
    		down(now, l, r);
    		ll mid = (l + r) >> 1;
    		if (L <= mid) insert(now << 1, l, mid, L, R, va);
    		if (mid < R) insert(now << 1 | 1, mid + 1, r, L, R, va);
    		up(now);
    	}
    	
    	ll query(ll now, ll l, ll r, ll L, ll R) {
    		if (L <= l && r <= R) return v[now];
    		down(now, l, r);
    		ll mid = (l + r) >> 1, re = 0;
    		if (L <= mid) re += query(now << 1, l, mid, L, R);
    		if (mid < R) re += query(now << 1 | 1, mid + 1, r, L, R);
    		return re;
    	}
    }T;
    
    int main() {
    //	freopen("sorrow.in", "r", stdin);
    //	freopen("sorrow.out", "w", stdout);
    	
    	scanf("%lld %lld", &n, &m);
    	for (ll i = 1; i <= n; i++) scanf("%lld", &a[i]);
    	
    	T.build(1, 1, n);
    	
    	ll lim = sqrt(n);
    	while (m--) {
    		scanf("%lld", &op);
    		if (op == 1) {
    			scanf("%lld %lld %lld %lld", &k, &x, &y, &z);
    			if (k <= lim) {//小的直接暴力前缀和
    				for (ll i = x; i <= y; i++) {
    					f[k][i] += z;
    				}
    				g[k][0] = f[k][0];
    				for (int i = 1; i < k; i++)
    					g[k][i] = g[k][i - 1] + f[k][i];
    				sum[k] += (y - x + 1) * z;
    			}
    			else {
    				for (ll i = 0; i + x <= n; i += k)
    					T.insert(1, 1, n, i + x, min(n, i + y), z);
    			}
    		}
    		if (op == 2) {
    			scanf("%lld %lld", &x, &y);
    			ans = T.query(1, 1, n, x, y);
    			for (ll i = 1; i <= lim; i++) {
    				ll nm1 = x / i, nm2 = y / i;
    				ans += sum[i] * (nm2 - nm1);
    				ans += g[i][y % i];//加上后面的一节,减去前面多算的一节
    				if (x % i != 0) ans -= g[i][x % i - 1];
    			}
    			printf("%lld
    ", ans);
    		}
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    验证码识别思路
    chrome插件 图片搜索(20130602更新版本v2.0)
    CoffeeScript实现BF解释器
    浏览器几种高度宽度
    javascript Event事件
    浮躁的心
    前端页面跨域访问
    firefox浏览器兼容event事件方法
    转:Java Annotation详解
    javascript void(0)问题【转】
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/YBTOJ_GXJJ_21290.html
Copyright © 2020-2023  润新知