您的位置:首页 > 编程语言 > C语言/C++

CSU-ACM2017暑期训练16-树状数组 C - 按钮控制彩灯实验 CSU - 1770

2017-08-14 21:58 537 查看

C - 按钮控制彩灯实验

应教学安排,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


放假前困扰多时的题目如今再次遇到,终于会做了。

由于区间极大,又暗含奇偶特性,故使用初值赋为零的树状数组记录每次滑动的起始点,对起始点加一,对结束点之后的一点减一。这样操作后,对前x项求和,影响只局限于被按过开关的区间。详细地说,假如共有10盏灯,第一次操作是划过区间 [4,6] 的开关,对树状数组元素 c[4] 加一,对树状数组元素 c[6] 减一,对于这个区间左侧的所有灯,sum(x)=0,x∈[1,3];对区间中的所有灯,sum(x)=1,x∈[4,6];对于区间右侧的所有灯,sum(x)=0,x∈[7,10]。

由此,我们求出前n项的和即知第n盏灯经历了几次操作。若这个操作数是奇数,就将这盏灯的原始状态与1异或(使得0变1,1变0);若这个操作数是偶数,就保持这盏灯的原始状态。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
#include <queue>
using namespace std;
const int maxn = 1e5+10;

int n, t;
int C[maxn], A[maxn];

void add(int x, int d){
while(x <= n){
C[x] += d; x += x&-x;
}
}

int sum(int x){
int ret = 0;
while(x > 0){
ret += C[x]; x -= x&-x;
}
return ret;
}

int main(){
#ifdef TEST
freopen("test.txt", "r", stdin);
#endif // TEST

while(cin >> n >> t){
memset(A, 0, sizeof(A));
memset(C, 0, sizeof(C));
for(int i = 1; i <= n; i++)
scanf("%d", &A[i]);
for(int i = 1; i <= t; i++){
int s, e;
scanf("%d%d", &s, &e);
add(s, 1);
add(e+1, -1);
}
for(int i = 1; i <= n; i++)
printf("%d%c", sum(i)&1 ? A[i]^1 : A[i], " \n"[i==n]);
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++ 树状数组