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;
    }
    
  • 相关阅读:
    Linux 内核源码中likely()和unlikely()【转】
    详解likely和unlikely函数【转】
    [arm驱动]Linux内核开发之阻塞非阻塞IO----轮询操作【转】
    Linux时间子系统之六:高精度定时器(HRTIMER)的原理和实现【转】
    Linux下的hrtimer高精度定时器【转】
    Linux 高精度定时器hrtimer 使用示例【转】
    Linux 进程等待队列【转】
    【转】【Android】对话框 AlertDialog -- 不错不错
    【转】 CATransform3D 矩阵变换之立方体旋转实现细节
    【转】如何在IOS中使用3D UI
  • 原文地址:https://www.cnblogs.com/Bn_ff/p/12832709.html
Copyright © 2011-2022 走看看