/***************************\ * @prob: poj2155 Matrix * * @auth: Wang Junji * * @stat: Accepted. * * @date: May. 29th, 2012 * * @memo: 树状数组 * \***************************/ #include <cstdio> #include <cstdlib> #include <algorithm> #include <cstring> #include <string> const int maxN = 1010; bool a[maxN][maxN]; int n, m, T; inline void modify(int x, int y) { for (int i = x; i; i -= i & -i) for (int j = y; j; j -= j & -j) a[i][j] ^= 1; return; } inline bool query(int x, int y) { bool ans = 0; for (int i = x; i < n + 1; i += i & -i) for (int j = y; j < n + 1; j += j & -j) ans ^= a[i][j]; return ans; } int main() { freopen("Matrix.in", "r", stdin); freopen("Matrix.out", "w", stdout); scanf("%d", &T); while (T--) { scanf("%d%d", &n, &m); for (int i = 1; i < n + 1; ++i) for (int j = 1; j < n + 1; ++j) a[i][j] = 0; while (m--) switch (scanf("\n"), getchar()) { case 'C': { int x0, y0, x1, y1; scanf("%d%d%d%d", &x0, &y0, &x1, &y1); modify(x1, y1); modify(x0 - 1, y1); modify(x1, y0 - 1); modify(x0 - 1, y0 - 1); } break; case 'Q': { int x, y; scanf("%d%d", &x, &y); printf("%d\n", query(x, y)); } break; } printf("\n"); } return 0; } /* 询问的是某个点,改变的是区间,只需要用递减式枚举来修改,用递增式枚举来询问即可。 */poj3321 Apple Tree
/*******************************\ * @prob: poj3321 Apple Tree * * @auth: Wang Junji * * @stat: Accepted. * * @date: May. 30th, 2012 * * @memo: 树状数组、维护Dfs序 * \*******************************/ #include <cstdio> #include <cstdlib> #include <algorithm> #include <cstring> #include <string> const int maxN = 100010; struct Edge { int v; Edge *next; Edge() {} Edge(int v, Edge *next): v(v), next(next) {} } *edge[maxN]; bool ex[maxN]; int a[maxN], DFN[maxN], sz[maxN], n, m; void Dfs(int u, int Last) { static int tot = 0; DFN[u] = ++tot; sz[u] = 1; for (Edge *p = edge[u]; p; p = p -> next) if (p -> v - Last) Dfs(p -> v, u), sz[u] += sz[p -> v]; return; } inline void chg(int x) { ex[x] ^= 1; for (int i = DFN[x]; i < n + 1; i += i & -i) ex[x] ? ++a[i] : --a[i]; return; } inline int sum(int x) { int ans = 0; for (int i = DFN[x] + sz[x] - 1; i; i -= i & -i) ans += a[i]; for (int i = DFN[x] - 1; i; i -= i & -i) ans -= a[i]; return ans; } int main() { freopen("Apple_Tree.in", "r", stdin); freopen("Apple_Tree.out", "w", stdout); scanf("%d", &n); for (int i = 1, u, v; i < n; ++i) { scanf("%d%d", &u, &v); edge[u] = new Edge(v, edge[u]); edge[v] = new Edge(u, edge[v]); } Dfs(1, 0); for (int i = 1; i < n + 1; ++i) chg(i); scanf("%d", &m); for (int x; m--;) switch (scanf("\n"), getchar()) { case 'C': scanf("%d", &x); chg(x); break; case 'Q': scanf("%d", &x); printf("%d\n", sum(x)); break; } return 0; }poj2481 Cows
/*****************************\ * @prob: poj2481 Cows * * @auth: Wang Junji * * @stat: Accepted. * * @date: May. 31st, 2012 * * @memo: 树状数组 * \*****************************/ #include <cstdio> #include <cstdlib> #include <algorithm> #include <cstring> #include <string> const int maxN = 100010; int a[maxN], ans[maxN], L[maxN], R[maxN], ord[maxN], n, N; inline int getint() { int res = 0; char tmp; while (!isdigit(tmp = getchar())); do res = (res << 3) + (res << 1) + tmp - '0'; while (isdigit(tmp = getchar())); return res; } inline bool cmp(const int &a, const int &b) {return R[a] > R[b] || (R[a] == R[b] && L[a] < L[b]);} inline void Add(int x) {for (int i = x; i < N + 1; i += i & -i) ++a[i]; return;} inline int sum(int x) { int ans = 0; for (int i = x; i; i -= i & -i) ans += a[i]; return ans; } int main() { freopen("cow.in", "r", stdin); freopen("cow.out", "w", stdout); while (n = getint()) { memset(a, 0, sizeof a); N = 0; for (int i = 0; i < n; ord[i] = i, ++i) N = std::max(N, L[i] = getint() + 1), R[i] = getint() + 1; std::sort(ord, ord + n, cmp); for (int i = 0, ths; i < n; ++i) { ths = ans[ord[i]] = sum(L[ord[i]]), Add(L[ord[i]]); while (L[ord[i + 1]] == L[ord[i]] && R[ord[i + 1]] == R[ord[i]]) ans[ord[++i]] = ths, Add(L[ord[i]]); //计算后面相同的区间。 } for (int i = 0; i < n; ++i) printf("%d ", ans[i]); printf("\n"); } return 0; } /* 先把区间按右界从大到小排序(右界相同时按左界从小到大排序),然后按照Stars的做法继续做。 注意多个完全相同的区间在一起时,要同时把他们都计算出来,并且多次计数。 */
poj3067 Japan
/***************************\ * @prob: poj3067 Japan * * @auth: Wang Junji * * @stat: Accepted. * * @date: May. 31st, 2012 * * @memo: 树状数组 * \***************************/ #include <cstdio> #include <cstdlib> #include <algorithm> #include <cstring> #include <string> const int maxN = 1000010; int L[maxN], R[maxN], ord[maxN], n, m, K; long long a[maxN], ans; inline int getint() { int res = 0; char tmp; while (!isdigit(tmp = getchar())); do res = (res << 3) + (res << 1) + tmp - '0'; while (isdigit(tmp = getchar())); return res; } inline bool cmp(const int &a, const int &b) {return R[a] > R[b] || (R[a] == R[b] && L[a] > L[b]);} inline void Add(int x) {for (int i = x; i < n + 1; i += i & -i) ++a[i]; return;} inline long long sum(int x) { long long ans = 0; for (int i = x; i; i -= i & -i) ans += a[i]; return ans; } int main() { freopen("Japan.in", "r", stdin); freopen("Japan.out", "w", stdout); for (int T = getint(), t = 0; t < T;) { memset(a, 0, sizeof a); ans = 0; n = getint(); m = getint(); K = getint(); for (int i = 0; i < K; ord[i] = i, ++i) L[i] = getint(), R[i] = getint(); std::sort(ord, ord + K, cmp); for (int i = 0; i < K; ++i) ans += sum(L[ord[i]] - 1), Add(L[ord[i]]); printf("Test case %d: %lld\n", ++t, ans); } return 0; } /* 先按第二个坐标从大到小排序(若第二个坐标相同则按第一个坐标从大到小排序),然后按照Stars或者Cow的做法继续做就是了。 看错一个题目条件,"At most two superhighways cross at one location."并不一定是一个城市只向两个城市建设高速公路。 数组要开够,10^6。 要用long long类型存储。 */poj2029 Get Many Persimmon Trees
/********************************************\ * @prob: poj2029 Get Many Persimmon Trees * * @auth: Wang Junji * * @stat: Accepted * * @date: May. 31th, 2012 * * @memo: 二维树状数组 * \********************************************/ #include <cstdio> #include <cstdlib> #include <algorithm> #include <cstring> #include <string> const int maxN = 1010; int a[maxN][maxN], n, m, K; inline int getint() { int res = 0; char tmp; while (!isdigit(tmp = getchar())); do res = (res << 3) + (res << 1) + tmp - '0'; while (isdigit(tmp = getchar())); return res; } inline void Add(int x, int y) { for (int i = x; i < n + 1; i += i & -i) for (int j = y; j < m + 1; j += j & -j) ++a[i][j]; return; } inline int sum(int x, int y) { int ans = 0; for (int i = x; i; i -= i & -i) for (int j = y; j; j -= j & -j) ans += a[i][j]; return ans; } int main() { freopen("tree.in", "r", stdin); freopen("tree.out", "w", stdout); while (K = getint()) { n = getint(); m = getint(); memset(a, 0, sizeof a); for (int i = 0; i < K; ++i) { int x = getint(), y = getint(); Add(x, y); } int N = getint(), M = getint(), ans = 0; for (int i = N; i < n + 1; ++i) for (int j = M; j < m + 1; ++j) ans = std::max(ans, sum(i, j) + sum(i - N, j - M) - sum(i - N, j) - sum(i, j - M)); printf("%d\n", ans); } return 0; }
poj1990 MooFest
/***************************\ * @prob: poj1990 MooFest * * @auth: Wang Junji * * @stat: Accepted. * * @date: May. 31st, 2012 * * @memo: 树状数组 * \***************************/ #include <cstdio> #include <cstdlib> #include <algorithm> #include <cstring> #include <string> const int maxN = 200010; typedef long long int64; struct Cow {int vol, x;} cow[maxN]; int64 cnt_x[maxN], sum_x[maxN], ans, tot; int n, N; inline bool cmp(const Cow &a, const Cow &b) {return a.vol < b.vol;} inline void Add(int64 *a, int x, int delta) {for (int i = x; i < N; i += i & -i) a[i] += delta; return;} inline int64 sum(int64 *a, int x) { int64 ans = 0; for (int i = x; i; i -= i & -i) ans += a[i]; return ans; } int main() { freopen("MooFest.in", "r", stdin); freopen("MooFest.out", "w", stdout); scanf("%d", &n); for (int i = 0; i < n; ++i) scanf("%d%d", &cow[i].vol, &cow[i].x), N = std::max(N, cow[i].x + 1); std::sort(cow, cow + n, cmp); for (int i = 0; i < n; tot += cow[i].x, ++i) ans += cow[i].vol * (((sum(cnt_x, cow[i].x - 1) << 1) - i) * cow[i].x + tot - (sum(sum_x, cow[i].x - 1) << 1)), Add(cnt_x, cow[i].x, 1), Add(sum_x, cow[i].x, cow[i].x); printf("%lld\n", ans); return 0; } /* 用按音量排序(为了保证每次都用较大音量奶牛的音量来计算),用两个树状数组分别维护在当前奶牛左边的奶牛的个数以及横坐标之和。 那么每次左右两边分别计算距离之和(左边的奶牛数 * x - 左边的奶牛横坐标之和 + 右边的奶牛横坐标之和 - 右边的奶牛数 * x)再乘上当前奶牛的音量并累加即可。 其中,右边的奶牛横坐标之和 = 比当前奶牛音量小的奶牛的横坐标之和 - 左边的奶牛横坐标之和; 右边的奶牛数 = 比当前奶牛音量小的奶牛的横坐标之和 - 左边的奶牛数。 注意要用long long类型存储。 */hdu1166 敌兵布阵
/****************************\ * @prob: hdu1166 敌兵布阵 * * @auth: Wang Junji * * @stat: Accepted. * * @date: June. 21st, 2012 * * @memo: 树状数组 * \****************************/ #include <cstdio> #include <cstdlib> #include <algorithm> #include <cstring> #include <string> const int maxN = 50010; int a[maxN], n, T; char str[20]; inline void Add(int x, int val) { for (int i = x; i < n + 1; i += i & -i) a[i] += val; return; } /* Add */ inline int calc(int x) { int res = 0; for (int i = x; i; i -= i & -i) res += a[i]; return res; } /* calc */ int main() { freopen("enemy.in", "r", stdin); freopen("enemy.out", "w", stdout); scanf("%d", &T); while (T--) { scanf("%d", &n); int tmp, x, y; memset(a, 0, sizeof a); for (int i = 1; i < n + 1; ++i) scanf("%d", &tmp), Add(i, tmp); static int Case = 0; printf("Case %d:\n", ++Case); while (scanf("%s", str), strcmp(str, "End")) { scanf("%d%d", &x, &y); if (!strcmp(str, "Query")) printf("%d\n", calc(y) - calc(x - 1)); else if (!strcmp(str, "Add")) Add(x, y); else Add(x, -y); } /* while */ } /* while */ return 0; } /* main */ /* 树状数组的入门题,不再多说。 */hdu3333 Turning Tree
/********************************\ * @prob: hdu3333 Turning Tree * * @auth: Wang Junji * * @stat: Accepted. * * @date: June. 22nd, 2012 * * @memo: 树状数组、离散化处理 * \********************************/ #include <cstdio> #include <cstdlib> #include <algorithm> #include <cstring> #include <string> const int maxN = 100010; typedef long long int64; struct Seg { int L, R, ord; Seg() {} /* Seg */ Seg(int L, int R, int ord): L(L), R(R), ord(ord) {} /* Seg */ bool operator<(const Seg& b) const {return R < b.R || (R == b.R && L < b.L);} /* operator< */ } seg[maxN]; int64 tr[maxN], res[maxN]; int num[maxN], tab[maxN], pre[maxN], n, m, T; inline void Add(int x, int val) { for (int i = x; i && i < n + 1; i += i & -i) tr[i] += val; return; } /* Add */ inline int64 calc(int x) { int64 res = 0; for (int i = x; i; i -= i & -i) res += tr[i]; return res; } /* calc */ int main() { freopen("tree.in" , "r", stdin ); freopen("tree.out", "w", stdout); for (scanf("%d", &T); T--;) { scanf("%d", &n); for (int i = 1; i < n + 1; ++i) scanf("%d", num + i), tab[i - 1] = num[i]; std::sort(tab, tab + n); int cnt = std::unique(tab, tab + n) - tab; scanf("%d", &m); for (int i = 0, L, R; i < m; ++i) scanf("%d%d", &L, &R), seg[i] = Seg(L, R, i); std::sort(seg, seg + m); int pos = 0; memset(pre, 0, sizeof pre); memset(tr , 0, sizeof tr ); for (int i = 0; i < m; ++i) { while (pos < seg[i].R) { int Index = std::lower_bound(tab, tab + cnt, num[++pos]) - tab; Add(pre[Index] , -num[pos]); Add(pre[Index] = pos, num[pos]); } /* while */ res[seg[i].ord] = calc(seg[i].R) - calc(seg[i].L - 1); } /* for */ for (int i = 0; i < m; ++i) printf("%lld\n", res[i]); } /* for */ return 0; } /* main */ /* 刚看了题,就开始想在线算法,发现没法做。 一不小心看了一篇题解之后顿悟。 将所有询问的区间按照右界从小到大的顺序排序,记录每个数上一次出现的位置,每次将之前位置的数删掉,加入最新,用树状数组求和即可。 */