目录
CodeForces 706B Interesting drink
力扣 308. 二维区域和检索 - 矩阵可修改(单点更新区间查询)
一,树状数组
1,树状数组
树状数组,也叫二叉索引树(Binary Indexed Tree)

如图,A是基本数组,C是求和数组,
其中,C[1]=A[1], C[2]=C[1]+A[2], C[3]=A[3], C[4]=C[2]+C[3]+A[4]......C[8]=C[4]+C[6]+C[7]+A[8]......
树状数组最简单最经典的使用场景,就是单点更新区间查询。
2,区间更新单点查询
对于一段区间内增加某个值的操作,可以利用差分,转换成单点更新,把单点更新,转换成前缀和查询。
3,离散化
参考离散化
二,模板代码
#include<iostream>
#include<string.h>
using namespace std;
template<int maxLen = 100000>
class TreeArray
{
public:
TreeArray(int len)//len是元素实际数量,元素id范围是[1,n]
{
this->n = len;
memset(c, 0, sizeof(int)*(len + 1));
}
void add(int i, int x)
{
while (i <= n)
{
c[i] += x;
i += (i&(-i));
}
}
int getSum(int i)
{
int s = 0;
while (i)
{
s += c[i];
i -= (i&(-i));
}
return s;
}
private:
int n;
int c[maxLen+5];
};
三,OJ实战
CodeForces 706B Interesting drink
题目:
Description
Vasiliy likes to rest after a hard work, so you may often meet him in some bar nearby. As all programmers do, he loves the famous drink "Beecola", which can be bought in n different shops in the city. It's known that the price of one bottle in the shop i is equal to xi coins.
Vasiliy plans to buy his favorite drink for q consecutive days. He knows, that on the i-th day he will be able to spent mi coins. Now, for each of the days he want to know in how many different shops he can buy a bottle of "Beecola".
Input
The first line of the input contains a single integer n (1 ≤ n ≤ 100 000) — the number of shops in the city that sell Vasiliy's favourite drink.
The second line contains n integers xi (1 ≤ xi ≤ 100 000) — prices of the bottles of the drink in the i-th shop.
The third line contains a single integer q (1 ≤ q ≤ 100 000) — the number of days Vasiliy plans to buy the drink.
Then follow q lines each containing one integer mi (1 ≤ mi ≤ 109) — the number of coins Vasiliy can spent on the i-th day.
Output
Print q integers. The i-th of them should be equal to the number of shops where Vasiliy will be able to buy a bottle of the drink on the i-th day.
Sample Input
Input
5
3 10 8 6 11
4
1
10
3
11
Output
0
4
1
5
这个题目就是,给出若干查询,对于每个查询,输出数组中有多少个数比它小。
这不就是最典型的树状数组吗?
代码:
#include <iostream>
#include <string.h>
using namespace std;
int main()
{
int nn, a, q;
scanf("%d", &nn);
TreeArray<100000> opt(100000);
for (int i = 0; i < nn; i++)
{
scanf("%d", &a);
opt.add(a, 1);
}
scanf("%d", &q);
for (int i = 0; i < q; i++)
{
scanf("%d", &a);
if (a >= 100000)printf("%d\n", opt.getSum(100000));
else printf("%d\n", opt.getSum(a));
}
return 0;
}
CSU 1770 按钮控制彩灯实验
题目:
Description
应教学安排,yy又去开心的做电学实验了。实验的内容分外的简单一串按钮通过编程了的EEPROM可以控制一串彩灯。然而选择了最low的一种一对一的控制模式,并很快按照实验指导书做完实验的yy马上感觉到十分无趣。于是他手指在一排按钮上无聊的滑来滑去,对应的彩灯也不断的变化着开关。已知每一个按钮按下会改变对应一个彩灯的状态,如此每次yy滑动都会改变一串彩灯的状态。现已知彩灯最初的状态,以及yy n次无聊的滑动的起点和终点l,r。现问彩灯最终的状态。
Input
有多组数据。
每组数据第一行,n(1<=n<=10^5)代表彩灯串长度,t(0<=t<=10^5)代表yy滑动的次数
第二行n个数(0表示灭1表示亮)给出n个彩灯的目前的状态。
之后t行每行两个数li,ri(1<=li<=ri<=n)代表每次滑动的区间。
Output
每组用一行输出最终的串的状态,格式见样例。
Sample Input
3 2
1 0 1
1 3
2 3
Sample Output
0 0 1
首先说下这个题目的思路。
肯定不能用暴力的θ(n*t)的方法。
很明显,这个题目是有规律的。
在区间左侧和区间右侧都没有被改变,只有区间中间的彩灯被改变了。
而且这个题目最后只需要判断奇偶性,所以彩灯被改变了多少次,就是直接等于有多少个区间端点在它的左侧。
例如,本题的2个区间是【1,3】【2,3】,
彩灯1的左侧有1个端点,彩灯2的左侧有2个端点,彩灯3的左侧有0个端点
注意!计算左侧的端点数量的时候,区间左端点刚好等于彩灯的情况是要算进去的,
但是区间右端点刚好等于彩灯的情况是不能算进去的。
再比如一个任意的例子,
8 1
0 0 0 0 0 0 0 0
3 6
很明显答案应该是0 0 1 1 1 1 0 0
要说这个例子找规律应该不难。第1、2个彩灯左边有0个区间端点,
第3、4、5、6个区间端点左边有1个端点,第7、8个彩灯左边有2个端点。
重复一遍:彩灯3计入了左端点3,彩灯6不计入右端点6。
其实还有一种思路:区间【3,6】这个操作可以分解成2个操作,
1,改变前6个彩灯的状态,2,改变前3个彩灯的状态。
讲道理,这个想法应该更自然,而且更容易想到。
不过思考无非就是平时的积累(装逼了哈哈哈哈,其实就是碰巧玩过一个类似的游戏,点亮所有的灯)加上关键时刻的灵感,
看到这个题目的时候,我的灵感就是数端点数目,
仔细一想才有了这种把操作分解的想法。
这个题目的操作是无序而且互相独立的,所以只需要统计每个彩灯的左边一共出现过多少端点就可以了。
所以这个题目有2种方案,都AC了。
第一种:树状数组。
代码:
#include<iostream>
using namespace std;
int list__[100001];
int main()
{
int n,t, a;
while (cin >> n >> t)
{
TreeArray opt(n);
for (int i = 1; i <= n; i++)
{
cin >> list__[i];
}
while (t--)
{
cin >> a;
opt.add(a, 1);
cin >> a;
opt.add(a, 1);
}
for (int i = 1; i <= n; i++)
{
cout << (list__[i] + opt.getSum(i)) % 2;
if (i < n)cout << " ";
}
cout << endl;
}
return 0;
}
我把关闭同步的语句注释掉了,否则直接runtime error。。。。。。
然而,这个题目有一个更简单的方法,不需要任何数据结构,而且还略高效一点点。
因为这个题目就是恰好n次查询,所以用树状数组不如直接用最普通的数组了。
代码:
#include<iostream>
using namespace std;
int n;
int list[100001];
int change[100002];
int main()
{
int t, a;
while (cin >> n >> t)
{
for (int i = 1; i <= n; i++)
{
cin >> list[i];
change[i] = 0;
}
while (t--)
{
cin >> a;
change[a]++;
cin >> a;
change[a + 1]++;
}
change[0] = 0;
for (int i = 1; i <= n; i++)
{
change[i] += change[i - 1];
cout << (list[i] + change[i]) % 2;
if (i < n)cout << " "

最低0.47元/天 解锁文章
3149

被折叠的 条评论
为什么被折叠?



