D: Persistent Bookcase
tag:dfs
/*
题意:给你一个n*m的bool型矩阵,刚开始矩阵全是0, 下面需要对矩阵进行一些操作, 输出每一次操作后矩阵的1的个数
1 i j将矩阵的第i行j列变为1,如果mat[i][j] = 0的话
2 i j将矩阵的第i行j列变为0, 如果mat[i][j]=1的话
3 k 将第k行的0- >1 1->0
4 k返回k次操作后的状态, 若k = 0, 则返回全为0的状态
tag:dfs
分析:这些操作有明显的前后关系, 对于前三种操作是前面的状态转移到后面的状态而第四种则是由第k次操作后转移过来, 因此我们
可以根据这个建树, 然后直接在dfs的时候模拟即可。
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int maxq = 100000 + 100;
int n, m, q;
vector<int> G[maxq];
struct Qu{
int op, i, j;
}qu[maxq];
bool book[1000 + 10][1000 + 10];
int ans;
int res[maxq];
void dfs(int u) {
bool did = false;
if(u != 0) {
if(qu[u].op == 1 && !book[qu[u].i][qu[u].j]) { //放书
ans += 1; did = true;
book[qu[u].i][qu[u].j] ^= 1;
}else if(qu[u].op==2 && book[qu[u].i][qu[u].j]){ //拿书
ans -= 1; did = true;
book[qu[u].i][qu[u].j] ^= 1;
}else if(qu[u].op==3) {
int r = qu[u].i;
for(int i=1; i<=m; i++) {
if(book[r][i]) ans -= 1; else ans += 1;
book[r][i] ^= 1;
}
}
res[u] = ans;
}
for(int i=0; i<G[u].size(); i++) dfs(G[u][i]);
if(u!=0) {
if(qu[u].op==1 && did) {
ans -= 1; book[qu[u].i][qu[u].j] ^= 1;
}else if(qu[u].op==2 && did) {
ans += 1; book[qu[u].i][qu[u].j] ^= 1;
}else if(qu[u].op == 3) {
int r = qu[u].i;
for(int i=1; i<=m; i++) {
if(book[r][i]) ans -= 1; else ans += 1;
book[r][i] ^= 1;
}
}
}
}
int main() {
scanf("%d%d%d", &n, &m, &q);
for(int i=1; i<=q; i++) {
int op; scanf("%d", &op);
if(op == 1) { //i 行 j列放书 如果没有的话
int ii, jj; scanf("%d%d", &ii, &jj);
qu[i] = (Qu){op, ii, jj};
G[i-1].push_back(i);
}else if(op == 2) { //i行 j列消书如果有的话
int ii, jj; scanf("%d%d", &ii, &jj);
qu[i] = (Qu){op, ii, jj};
G[i-1].push_back(i);
}else if(op == 3) { //第i行翻转
int ii; scanf("%d", &ii);
qu[i].op = op; qu[i].i = ii;
G[i-1].push_back(i);
}else if(op == 4) { //转移到以前
int k; scanf("%d", &k);
qu[i].op = op; qu[i].i = k;
G[k].push_back(i);
}
}
dfs(0);
for(int i=1; i<=q; i++) {
printf("%d\n", res[i]);
}
return 0;
}
E:Garlands
/*
题目意思是给你一个n行m列的矩阵,矩阵中有一些链, 每个链的每个节点都有一些权值, 现在给你两个操作, switch i, 改变第i个链的状态
即若此链是关闭的那么就打开, 如果是打开的那么就关闭。。 ASK x1, y1, x2, y2 查询这个矩形区域内的权值和。
分析:
我们使用二维树状数组来查询矩形区域内的权值和,对于switch操作只需标记, 然后ASK前暴力维护值即可。
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
typedef long long LL;
const int maxn = 2000 + 100;
int n, m, k;
struct Bulb{
int x, y, pleasure;
Bulb() {}
Bulb(int x, int y, int pleasure):x(x), y(y), pleasure(pleasure) {}
};
vector<Bulb> G[maxn];
bool state[maxn];
bool st[maxn];
LL c[maxn][maxn], N;
int lowbit(int x) {
return x&(-x);
}
void add(int x, int y, int data) {
for(int i=x; i<N; i+=lowbit(i))
for(int j=y; j<N; j+=lowbit(j))
c[i][j] += data;
}
LL sum(int x, int y) {
LL res = 0;
for(int i=x; i>0; i-=lowbit(i))
for(int j=y; j>0; j-=lowbit(j))
res += c[i][j];
return res;
}
int main() {
scanf("%d%d%d", &n, &m, &k);
N = max(n, m) + 1;
for(int i=1; i<=k; i++) {
int len; scanf("%d", &len);
while(len--) {
int x, y, plea; scanf("%d%d%d", &x, &y, &plea);
G[i].push_back(Bulb(x, y, plea));
add(x, y, plea);
}
state[i] = 1;
st[i] = 1;
}
int q; scanf("%d", &q);
for(int i=0; i<q; i++) {
char str[10];
scanf("%s", str);
if(str[0] == 'A') {
int x1, y1, x2, y2; scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
x1 -= 1; y1 -= 1;
for(int i=1; i<=k; i++) { //第i串灯
if(st[i] == state[i]) continue; //state之前的状态
for(int j=0; j<G[i].size(); j++) {
if(state[i] == 1) {
add(G[i][j].x, G[i][j].y, -G[i][j].pleasure);
}else{
add(G[i][j].x, G[i][j].y, G[i][j].pleasure);
}
}
state[i] ^= 1;
}
LL res = sum(x2, y2) - sum(x2, y1) - sum(x1, y2) + sum(x1, y1);
printf("%I64d\n", res);
}else{
int i; scanf("%d", &i);
st[i] ^= 1;
}
}
return 0;
}