zoukankan      html  css  js  c++  java
  • 【题解】P3374 【模板】树状数组 1

    P3374 【模板】树状数组 1

    声明:本博客所有题解都参照了网络资料或其他博客,仅为博主想加深理解而写,如有疑问欢迎与博主讨论✧。٩(ˊᗜˋ)و✧*。

    题目描述

    如题,已知一个数列,你需要进行下面两种操作:

    将某一个数加上 (x)

    求出某区间每一个数的和。

    前言

    感觉最近学东西真的太快了...挺多都是囫囵吞枣,如果没有深入理解的话,做题就很难想到。这也是为什么我平时学得挺开心,一旦做题就懵逼的一大原因吧。

    (\)


    (\)

    (Solution)

    树状数组方法就不说了

    关于 (CDQ),其实就是一个按照时间分治的东西(当然了,在某些题目里可以把某些条件转化为时间)

    例如对于这道题来说,每个操作都是按照时间表的顺序进行的,前面的修改可能会影响后面的查询,但前面的查询不可能被后面的修改影响

    那么我们可以尝试着把所有操作分成两个子问题,解决完每个子问题后,我们只需统计左区间对右区间的影响即可

    如何统计这个影响呢?

    我们注意到一个问题,对于某个询问操作 (a),有一个修改操作 (b) 对它产生影响,那么 (b) 除了在时间表上的位置要比 (a) 靠前之外,(b) 所修改的点的位置也应该在 (a) 所要查询的区间内的

    对于很多个区间,似乎不太好统计答案...于是我们可以把一个区间变为两个前缀和相减,把原来的一个询问操作 (a) 变为两个求前缀和的操作

    那么可以把左右区间的所有操作排个序,按照位置枚举各个操作,若修改操作 (b) 对某个前缀和操作 (a) 产生了影响,那么 (b) 一定对 (a) 即后面的所有前缀和操作都产生了影响,就可以用一个 (sum) 来累加产生的影响即可(感觉有点糊,具体看代码)

    快排的时间是 (O(nlog{n})) 的,则总复杂度会有两个 (log) ,会被卡掉

    但是我们可以发现,由于是递归实现,那么左区间和右区间一定都是有序的,就可以用归并排序,一边归并一边计算贡献

    完结撒花✿✿ヽ(°▽°)ノ✿

    (\)


    (\)

    (Code)

    #include<bits/stdc++.h>
    #define F(i, x, y) for(int i = x; i <= y; ++ i)
    #define ll long long
    using namespace std;
    ll read();
    const int N = 2e6 + 5;
    int n, m, cnt, tot;
    ll ans[N];
    struct order{
    	ll pos, k, val;//pos 为位置,k 为操作类型,val 为修改的值 / 询问的编号
    }p[N], tmp[N];
    void CDQ(int l, int r)
    {
    	if(l == r) return;
    	ll mid = (l + r) >> 1;
    	CDQ(l, mid), CDQ(mid + 1, r);
    	ll sum = 0, i = l, j = mid + 1, o = l - 1;//归并的两个指针
    	while(i <= mid && j <= r)
    		if(p[i].pos <= p[j].pos) 
    		{
    			if(p[i].k == 1) sum += p[i].val;//如果是左区间的,那么就累加影响
    			tmp[++ o] = p[i ++];
    		}
    		else 
    		{
    			if(p[j].k == 2) ans[p[j].val] -= sum;//此时累加的影响一定会对此处前缀和产生影响,因为那些操作的时间和位置都在此处之前
    			if(p[j].k == 3) ans[p[j].val] += sum;//同上
    			tmp[++ o] = p[j ++];
    		}
    	while(i <= mid) tmp[++ o] = p[i ++];
    	while(j <= r)
    	{
    		if(p[j].k == 2) ans[p[j].val] -= sum;
    		if(p[j].k == 3) ans[p[j].val] += sum;
    		tmp[++ o] = p[j ++];
    	}
    	F(i, l, o) p[i] = tmp[i];//最后整体归并完成
    }
    int main()
    {
    	n = read(), m = read(), tot = n;
    	F(i, 1, n) p[i].val = read(), p[i].pos = i, p[i].k = 1;//直接把原数组也看成一次操作
    	for(int i = 1, op; i <= m; ++ i)
    	{
    		op = read();
    		if(op == 1)
    			p[++ tot].k = 1, p[tot].pos = read(), p[tot].val = read();
    		else
    		{
    			p[++ tot].k = 2, p[tot].pos = read() - 1, p[tot].val = ++ cnt;
    			p[++ tot].k = 3, p[tot].pos = read(), p[tot].val = cnt;
    		}
    	}
    	CDQ(1, tot);
    	F(i, 1, cnt) printf("%lld
    ", ans[i]);
    	return 0;
    }
    ll read()
    {
    	int x = 0, f = 1;
    	char c = getchar();
    	while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    	return x * f;
    }
    
  • 相关阅读:
    ArrayList removeRange方法分析
    LinkedHashMap源码分析(基于JDK1.6)
    LinkedList原码分析(基于JDK1.6)
    TreeMap源码分析——深入分析(基于JDK1.6)
    51NOD 2072 装箱问题 背包问题 01 背包 DP 动态规划
    51 NOD 1049 最大子段和 动态规划 模板 板子 DP
    51NOD 1006 最长公共子序列 Lcs 动态规划 DP 模板题 板子
    8月20日 训练日记
    CodeForces
    CodeForces
  • 原文地址:https://www.cnblogs.com/Bn_ff/p/12832709.html
Copyright © 2011-2022 走看看