树状数组题目
pku 3321 Apple Tree http://poj.org/problem?id=3321
题意:
苹果树上有n个分叉,每个插上长着一个苹果,给出每个叉的关系,然后给出两种操作1:C x改变分叉x的苹果(如果有就拿走,如果没有就长出一个) Q x询问包括树叉x在内以及其子树的苹果数量;
思路:
才开始就在如何将树结构转化成线性数组结构然后用树状数组求解上难住了,最后看了一下解题报告,原来是首先边表(或邻接表)存树,然后dfs对每个节点重新编号每个节点对应着s,e两个编号。s是首先进入该点时的编号也即它本身的新标号(对应了线性数组的编号),e是搜索完子树后返回根节点的编号也即根节点本身所包含的子树的最大编号(这样在求解苹果数量时就好求了)
直接就是getsum(e[i]) - getsum(s[i] - 1)了;
View Code
#include <cstdio> #include <algorithm> #include <cstring> #define maxn 100007 using namespace std; int s[maxn],e[maxn]; int a[maxn]; int num; bool vt[maxn]; struct node { int v; int next; }g[maxn]; int head[maxn],cnt,n; void init() { int i; memset(head,-1,sizeof(head)); cnt = 0; for (i = 0; i <= n; ++i) { a[i] = 0; vt[i] = false; } num = 0; } void add(int u,int v) { g[cnt].v = v; g[cnt].next = head[u]; head[u] = cnt++; } void dfs(int pos) { s[pos] = ++num; for (int i = head[pos]; i != -1; i = g[i].next) { int v = g[i].v; dfs(v); } e[pos] = num; } int lowbit(int x) { return x&(-x); } void modify(int pos,int sc) { while (pos <= n) { a[pos] += sc; pos += lowbit(pos); } } int getsum(int pos) { int sum = 0; while (pos > 0) { sum += a[pos]; pos -= lowbit(pos); } return sum; } int main() { //freopen("d.txt","r",stdin); int x,y,i; int q; scanf("%d",&n); init(); //建树 for (i = 0; i < n -1; ++i) { scanf("%d%d",&x,&y); add(x,y); } //dfs重新编号 dfs(1); //树状数组典型操作 for (i = 1; i <= n; ++i) modify(i,1); char op[3]; scanf("%d",&q); while (q--) { scanf("%s%d",op,&x); if (op[0] == 'Q') { printf("%d\n",getsum(e[x]) - getsum(s[x] - 1)); } else { if (!vt[x]) { modify(s[x],-1); vt[x] = true; } else { modify(s[x],1); vt[x] = false; } } } return 0; }
pku 1195 Mobile phones http://poj.org/problem?id=1195
二位树状数组模板题目:
给出n*n的矩阵,有两种操作,
1 x,y,z 将(x,y)方格的数更新z 2 x1,y1,x2,y2求方格(x1,y1)到(x2,y2)中的总数。
注意求和公式后面要加上多减去的部分。
View Code
#include <cstdio> #include <algorithm> #include <cstring> #define maxn 1025 using namespace std; int c[maxn][maxn]; int n; int lowbit(int x) { return x&(-x); } void modify(int x,int y,int sc) { int i,j; for (i = x; i <= n; i += lowbit(i)) { for (j = y; j <= n; j += lowbit(j)) { c[i][j] += sc; } } } int getsum(int x,int y) { int i,j; int sum = 0; for (i = x; i > 0; i -= lowbit(i)) { for (j = y; j > 0; j -= lowbit(j)) { sum += c[i][j]; } } return sum; } int main() { //freopen("d.txt","r",stdin); int op,x1,y1,z,x2,y2; memset(c,0,sizeof(c)); while (scanf("%d",&op)) { if (op == 3) break; else if (op == 0) scanf("%d",&n); else if (op == 1) { scanf("%d%d%d",&x1,&y1,&z); modify(x1 + 1,y1 + 1,z); } else { scanf("%d%d%d%d",&x1,&y1,&x2,&y2); printf("%d\n",getsum(x2 + 1,y2 + 1) - getsum(x1,y2 + 1) - getsum(x2 + 1,y1) + getsum(x1,y1)); } } return 0; }
pku 2029 http://poj.org/problem?id=1195
题意:
给定一个W*H的矩形方格,然后给出n个柿子树所在矩形小方格的坐标,然后给你一个dx*dy的小矩形,求用此小矩形能够框住的最多的柿子树的个数;
思路:
二维树状数组模板题目,将有柿子树的点标记为1无的标记为0,然后O(N^2)枚举求和,取最小值即可。其实这里看直接用map[i][j]存从[1,1]到[i,j]的和暴力枚举也可。
View Code
#include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #define CL(a,num) memset(a,num,sizeof(a)) #define maxn 507 #define N 8 using namespace std; const int inf = 99999999; int c[maxn][maxn],n; int row,col; int lowbit(int i) { return i&(-i); } void modify(int x,int y) { int i,j; for (i = x; i <= row; i += lowbit(i)) { for (j = y; j <= col; j += lowbit(j)) { c[i][j] += 1; } } } int getsum(int x,int y) { int i,j; int sum = 0; for (i = x; i > 0; i -= lowbit(i)) { for (j = y; j > 0; j -= lowbit(j)) { sum += c[i][j]; } } return sum; } int main() { //freopen("din.txt","r",stdin); int i,j; int x,y,dx,dy; while (~scanf("%d",&n)) { if (n == 0) break; CL(c,0); scanf("%d%d",&row,&col); for (i = 0; i < n; ++i) { scanf("%d%d",&x,&y); modify(x,y); } scanf("%d%d",&dx,&dy); int sum = -inf; for (i = 1; i + dx - 1 <= row; ++i) { for (j = 1; j + dy - 1 <= col; ++j) { int x2 = i + dx - 1; int y2 = j + dy - 1; int x1 = i,y1 = j; int tmp = getsum(x2,y2) - getsum(x2,y1 - 1) - getsum(x1 - 1,y2) + getsum(x1 -1,y1 -1); //printf(">>%d\n",tmp); sum = max(sum,tmp); } } printf("%d\n",sum); } return 0; }