树状数组部分例题

poj-1037 

给出n个星星的坐标,如果一个星星的左下方(包含正左和正下)有k颗星星,就说这颗星星是k级的,统计每个等级有多少个点。这题可用树状数组,对于每个星星按y坐标从小到大排序,相同y坐标按x坐标从小到大排序(题目中数据已经有序),输入顺序已排好序,那么只要依次统计星星i之前x坐标小于等于i.x的星星有多少,即是星星i的级别
code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#include<string>
#include<set>
#include<cmath>
using namespace std;
#define N 15005
#define maxn 32002
int n;
int a[maxn], vis[N];
int lowb(int x) {
	return x & (-x);
}
void push_m(int x) {
	for (int i = x; i < maxn; i += lowb(i)) {
		a[i]++;
	}
}
int sear(int x) {
	int ans = 0;
	for (int i = x; i > 0; i -= lowb(i)) {
		ans += a[i];
	}
	return ans;
}
int main() {
	memset(a, 0, sizeof(a));
	memset(vis, 0, sizeof(vis));
	int x, y;
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		scanf("%d%d", &x, &y);
		vis[sear(x + 1)]++;
		push_m(x+1);
	}
	for (int i = 0; i < n; i++)
		cout << vis[i] << endl;
	return 0;
}

poj-3321

题意:一棵树上有n个叉子(其实就是n个节点),再告诉你n-1个线段告诉你是u和v节点相连的,再输入m组操作,有两种操作,当是Q操作时你就要输出对象的节点上有多少个苹果(初始化时每个节点都有苹果),C操作是吃掉苹果或是长出苹果(一个节点只能是有一个苹果或是没有苹果,即:有苹果就是吃掉当前节点的苹果,没苹果就是长出苹果)。

思路:可以把它倒过来就可以用树状数组做了,就是还要用dfs来搜一下每个节点的分支有多少个,开始的时间和结束的时间就可以了。
code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<vector>
#include<set>
using namespace std;
#define N 100005
vector<vector<int>>a(N);
int n, m;
int cnt;
int lef[N], rig[N], vis[N];
int c[N];
int lowbit(int x) {
	return x & (-x);
}
void put_m(int x,int d) {
	while (x <= n) {
		c[x] += d;
		x += lowbit(x);
	}
}
void dfs(int x) {
	lef[x] = cnt;
	for (int i = 0; i < a[x].size(); i++) {
		cnt++;
		dfs(a[x][i]);
	}
	rig[x] = cnt;
}
int sum(int x) {
	int res = 0;
	while (x > 0) {
		res += c[x];
		x -= lowbit(x);
	}
	return res;
}
int main() {
	memset(lef, 0, sizeof(lef));
	memset(rig, 0, sizeof(rig));
	memset(c, 0, sizeof(c));
	//memset(vis, 0, sizeof(vis));
	int u, v, x;
	char temp;
	scanf("%d", &n);
	for (int i = 0; i < n - 1; i++) {
		scanf("%d%d", &u, &v);
		a[u].push_back(v);
	}
	cnt = 1;
	dfs(1);
	for (int i = 1; i <= n; i++) {
		vis[i] = 1;
		put_m(i, 1);
	}
	scanf("%d", &m);
	cin.get();
	for (int i = 0; i < m; i++) {
		scanf("%c%d", &temp, &x);
		if (temp == 'Q') {
			cout << sum(rig[x]) - sum(lef[x] - 1) << endl;
		}
		else {
			if (vis[x]) {
				put_m(lef[x], -1);
				vis[x] = 0;
			}
			else {
				put_m(lef[x], 1);
				vis[x] = 1;
			}
		}
		cin.get();
	}
	return 0;
}

poj-2481

题目大意:

给你很多线段的头S和尾E,问每一条线段中包含了多少个线段,(S和E相同不计在内)。这题先一看,完全不知道什么方法,感觉非常的难办。

但是!树状数组可以轻松解决这个问题!!!首先,将她们线段的s和e当做是(s,e)一个点,这样子把所有点画出来,你就会发现一个很神奇的现象,题目要求就会变成:问每一个点的左上角有多少个点?
stars

code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
using namespace std;
#define N 100005
struct node {
	int s, e, k;
}a[N], b[N];
int c[N], res[N];
int n;
map<pair<int, int>, int>q;
bool cmp(node x, node y) {
	if (x.e == y.e) {
		return x.s < y.s;
	}
	return x.e > y.e;
}
int lowbit(int x) {
	return x & (-x);
}
void insert(int x, int m) {
	for (int i = x; i <= m; i += lowbit(i)) {
		c[i] += 1;
	}
}
int find(int x, int m) {
	int sum = 0;
	for (int i = x; i > 0; i -= lowbit(i)) {
		sum += c[i];
	}
	return sum;
}
int main() {
	while (scanf("%d", &n)) {
		if (n == 0)break;
		memset(c, 0, sizeof(c));
		int mx = -1;
		for (int i = 0; i < n; i++) {
			scanf("%d%d", &a[i].s, &a[i].e);
			a[i].s += 1;
			mx = max(a[i].s, mx);
			a[i].k = i;
		}
		sort(a, a + n, cmp);
		insert(a[0].s, mx);
		res[a[0].k] = 0;
		for (int i = 1; i < n; i++) {
			if (a[i].s == a[i - 1].s&&a[i].e == a[i - 1].e) {
				res[a[i].k] = res[a[i - 1].k];
				insert(a[i].s, mx);
				continue;
			}
			res[a[i].k] = find(a[i].s, mx);
			insert(a[i].s, mx);
		}
		for (int i = 0; i < n; i++) {
			if (i == 0)
				cout << res[i];
			else
				cout << " " << res[i];
		}cout << endl;
	}
	return 0;
}

poj-2464

题意:在平面直角坐标系中给你N个点,stan和ollie玩一个游戏,首先stan在竖直方向上画一条直线,该直线必须要过其中的某个点,然后ollie在水平方向上画一条直线,该直线的要求是要经过一个stan之前画过的点。 这时候平面就被分割成了四块,两个人这时候会有一个得分,stan的得分是平面上第1、3象限内的点的个数,ollie的得分是平面上第2、4象限内的点的个数,在统计的时候在所画线上的点都不计算在内。求最终stan使得自己的最差得分最高,并且输出此时ollie的得分。
https://blog.csdn.net/shiqi_614/article/details/8236745

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值