poj-线段树合集

http://poj.org/problem?id=2828

Sample Input

4
0 77
1 51
1 33
2 69
4
0 20523
1 19243
1 3890
0 31492

Sample Output

77 33 69 51
31492 20523 3890 19243

题目大意,插队的问题,每个案例给出n,代表有n个插队的,每个给出p,v,意思是代号为v的人插在了第p个人的后面,问最后的队伍的排列?

题目中一开始整个队列是空的,也就是说如果输入i,j:代表代号为j的人插在了第i个人的后面,也就是说在他之前一定有了i个人,而他的位置是i+1。

所以由后向前推到,每个遇到的都是确定位置的,最后的人选定的位置不会改变,同样因为是倒叙输入,在第i个人后插队,也就是说他的前面一定要留下i个空格。
code:

#include
#include
#include
#include
#include
#include
using namespace std;
#define N 210000
struct node {
	int v, p;
}a[N];
int ans = 0;
int n, ct[N<<2], res[N<<2];
void push_now(int l,int r,int rt) {
	if(l!=r)
	ct[rt] = ct[rt << 1] + ct[rt << 1 | 1];
}
void build(int l, int r, int rt) {
	ct[rt] = res[rt] = 0;
	if (l == r) {
		ct[rt] = 1;
		return;
	}
	int mid = (l + r) >> 1;
	build(l, mid, rt << 1);
	build(mid + 1, r, rt << 1|1);
	push_now(l,r,rt);
}
void insert(int x, int k, int l, int r, int rt) {
	if (l == r && ct[rt] == 1) {
		ct[rt] = 0;
		res[rt] = k;
		return;
	}
	if (x <= ct[rt << 1]) 
		insert(x, k, l, (l + r)/2, rt << 1);
	else 
		insert(x-ct[rt<<1], k, (l + r)/2 + 1,r, rt << 1|1);
	push_now(l,r,rt);
}
void putw(int l, int r, int rt) {
	if (l == r) {
		if (ans == 0)
			cout << res[rt];
		else
			cout << " " << res[rt];
		ans++;
		return;
	}
	int mid = (l + r) >> 1;
	putw(l, mid, rt << 1);
	putw(mid + 1, r, rt << 1 | 1);
}
int main() {
	while (scanf("%d", &n)!=EOF) {
		ans = 0;
		for (int i = 0; i < n; i++) {
			scanf("%d%d", &a[i].v, &a[i].p);
			a[i].v++;
		}
		build(1, n, 1);
		for (int i = n - 1; i >= 0; i--) {
			insert(a[i].v, a[i].p, 1, n, 1);
		}
		
		putw(1, n, 1);
		cout << endl;
	}
	return 0;
}

线段树-逆序对

对于数组A,A[i]的逆序对数量为i之前比它大的数的个数。如果已经知道A中的最大值max,当我们顺序的去求A[i]的逆序对的数量的时候,其实就是找此时A中i之前A[i]到max的数有多少个。当然,对于求区间内的事情,树状数组和线段树是最为擅长的。一下代码仅仅考虑A中的元素为整数的情况,如果数太大者有double的情形需要先离散化处理。

code:

#include
#include
#include
#include
#include
#include
using namespace std;
#define N 1100
struct node {
	int w, v;
}a[N];
int b[N], p[N << 2];
int lx[N], ln[N];
bool cmp(node x, node y) {
	return x.w < y.w;
}
int n;
void build(int l, int r, int rt) {
	p[rt] = 0;
	if (l == r) {
		return;
	}
	int mid = (l + r) >> 1;
	build(l, mid, rt << 1);
	build(mid + 1, r, rt << 1 | 1);
}
void insert(int l, int r, int rt, int k) {
	p[rt]++;
	if (l == r)return;
	int mid = (l + r) >> 1;
	if (k <= mid)insert(l, mid, rt << 1, k);
	else insert(mid + 1, r, rt << 1 | 1, k);
}
int search(int l, int r, int rt, int k) {
	if (k == r)return p[rt];
	if (k <= (l + r) >> 1)
		return search(l, (l + r) >> 1, rt << 1, k);
	else
		return p[rt << 1] + search(((l + r) >> 1) + 1, r, rt <<1 | 1, k);
}
int main() {
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		scanf("%d", &a[i].w);
		a[i].v = i;
	}
	sort(a, a + n,cmp);
	b[a[0].v] = 1;
	int ans = 1;
	for (int i = 1; i < n; i++) {
		if (a[i - 1].w == a[i].w) {
			b[a[i].v] = ans;
		}
		else {
			ans++;
			b[a[i].v] = ans;
		}
	}
	build(0, n, 1);
	for (int i = 0; i < n; i++) {
		lx[i] = search(0, n, 1, b[i]-1);
		insert(0, n, 1, b[i]);
	}
	build(0, n, 1);
	for (int i = n - 1; i >= 0; i--) {
		ln[i] = search(0, n, 1, b[i]-1);
		insert(0, n, 1, b[i]);
	}
	long long sum = 0;
	for (int i = 0; i < n; i++) {
		sum += (long long)ln[i] * lx[i];
	}
	cout << sum << endl;
	return 0;
}

poj-2777

http://poj.org/problem?id=2777

Sample Input

2 2 4
C 1 1 2
P 1 2
C 2 2 2
P 1 2

Sample Output

2
1

题目大意:给出T中颜色,可以给一段区域涂色,初始是全为1的颜色,然后有两种操作

(1)C x y z表示将区间x到y的颜色更改为z

(2)P x y 表示查询区间x到y的颜色种类。

题目看起来不符合区间和的条件,但是可以通过二进制转化一下。

初始化肯定都是颜色1,就表示只有一种颜色,然后每次更新颜色时,取这个数的a[x]=1<<(Item-1),a[x]中的1的位置就表示每个颜色的位置

然后pushup操作就改为:a[x]=a[x*2]|a[x*2+1],a[x]中的1的个数就是这个区间颜色的个数。

code:

#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N = 1e5 + 10;
int n, t, o;
int a[N << 2], b[N << 2];
void build(int l, int r, int rt) {
	a[rt] = 1;
	if (l == r) {
		return;
	}
	int mid = (l + r) >> 1;
	build(l, mid, rt << 1);
	build(mid + 1, r, rt << 1 | 1);
}
void push(int rt) {
	a[rt] = a[rt << 1] | a[rt << 1 | 1];
}
void pushdown(int rt) {
	if (b[rt]) {
		a[rt << 1] = b[rt];
		a[rt << 1 | 1] = b[rt];
		b[rt << 1] = b[rt];
		b[rt << 1 | 1] = b[rt];
		b[rt] = 0;
	}
}
void change(int l, int r, int x, int y, int rt, int c) {
	if (x <= l && y >= r) {
		a[rt] = 1 << (c - 1);
		b[rt] = 1 << (c - 1);
		return;
	}
	int mid = (l + r) >> 1;
	pushdown(rt);
	if (x <= mid)
		change(l, mid, x, y, rt << 1, c);
	if (y > mid)
		change(mid + 1, r, x, y, rt << 1 | 1, c);
	push(rt);
}
int pur(int l, int r, int x, int y, int rt) {
	if (x <= l && y >= r) {
		return a[rt];
	}
	int mid = (l + r) >> 1;
	pushdown(rt);
	int t1 = 0, t2 = 0, ts = 0;
	if (x <= mid) t1 = pur(l, mid, x, y, rt << 1);
	if (y > mid) t2 = pur(mid + 1, r, x, y, rt << 1 | 1);
	ts = t1 | t2;
	return ts;
}
int main() {
	int x, y, c;
	char str[10];
	scanf("%d%d%d", &n, &t, &o);
	memset(a, 0, sizeof(a));
	memset(b, 0, sizeof(b));
	build(1, n, 1);
	for (int i = 0; i < o; i++) {
		scanf("%s", str);
		if (str[0] == 'C') {
			scanf("%d%d%d", &x, &y, &c);
			if (x > y) {
				swap(x, y);
			}
			change(1, n, x, y, 1, c);
		}
		else {
			scanf("%d%d", &x, &y);
			if (x > y)swap(x, y);
			int ts = pur(1, n, x, y, 1);
			int cnt = 0;
			while (ts) {
				if (ts & 1)cnt++;
				ts >>= 1;
			}
			cout << cnt << endl;
		}
	}
	return 0;
}

poj-2886

http://poj.org/problem?id=2886

题目大意:给出n个人的姓名和手里的一个号码,n个人排成一圈,号码有正有负,代表着正向还是反向移动A个位置,比赛从第k个人开始,把被选到的人踢出,问按踢出的顺序中因子数最多的是谁?

建立线段树,把n个人被踢的顺序找到,然后求出n个人中因子数最多的(最小的数)是谁

题目用到反素数https://blog.csdn.net/winddreams/article/details/39323039

code:

#include
#include
#include
#include
#include
#include
using namespace std;
#define N 500005
int a[N], b[2333333];
int n, k, x, t, pos;
int ans, mx = 0;
int c[10] = { 2,3,5,7,11,13,17,19 };
void push(int rt) {
	b[rt] = b[rt << 1] + b[rt << 1 | 1];
}
void build(int l, int r, int rt) {
	if (l == r) {
		b[rt] = 1;
		return;
	}
	int mid = (l + r) >> 1;
	build(l, mid, rt << 1);
	build(mid + 1, r, rt << 1 | 1);
	push(rt);
}
int update(int l, int r, int rt, int h) {
	b[rt]--;
	if (l == r) {
		pos = l;
		return 1;
	}
	int mid = (l + r) >> 1;
	if (b[rt << 1] >= h) return update(l, mid, rt << 1, h);
	return update(mid+1, r, rt << 1 | 1, h - b[rt << 1])+b[rt<<1];
}
void dfs(int index, int tmp, int num) {
	if (index >= 8)return;
	if (num > mx) {
		mx = num;
		ans = tmp;
	}
	if (num == mx && tmp < ans)ans = tmp;
	for (int i = 1; i < 25; i++) {
		if (n / c[index] < tmp)break;
		tmp *= c[index];
		dfs(index + 1,tmp , num*(i + 1));
	}
}
int main() {
	string str[N];
	scanf("%d%d", &n, &k);
	cin.get();
	for (int i = 1; i <= n; i++) {
		cin >> str[i] >> a[i];
		cin.get();
	}
	dfs(0, 1, 1);
	build(1, n, 1);
	for(int cnt=1;cnt<=ans;cnt++){
		k = update(1, n, 1, k);
		if (cnt == ans) {
			cout << str[pos] << " " << mx << endl;
			break;
		}
		if (a[pos] > 0) --k;
		k = ((k + a[pos]) % (n - cnt) + (n - cnt)) % (n - cnt);
		if (k == 0) k = n - cnt;
	}
	return 0;
}

poj-1151

http://poj.org/problem?id=1151

题目大意:给你n个矩形,求面积并,点范围大,需要离散化

从左往右或者从上往下扫描,这里是从走往右。

讲解:https://www.cnblogs.com/arbitrary/p/3202630.html

高级数据结构 林厚从里的第143页 讲的很好。

code:

#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define N 10005
struct node {
	int lt, rt, c, s;
	double x, y1, y2, m;
}L[N], a[N];
int n;
double Y[N], re[N];
mapres;
bool cmp(node a, node b) {
	return a.x < b.x;
}
void build(int l, int r, int root) {
	a[root].lt = l, a[root].rt = r;
	a[root].m = a[root].c = 0;
	if (r - l <= 1)return;
	int mid = (l + r) >> 1;
	build(l, mid, root << 1);
	build(mid, r, root << 1 | 1);
}
void update(int root) {
	if (a[root].c > 0)
		a[root].m = re[a[root].rt] - re[a[root].lt];
	else
		if (a[root].rt - a[root].lt == 1)
			a[root].m = 0;
		else
			a[root].m = a[root << 1].m + a[root << 1 | 1].m;
}
void insert(int l, int r, int root) {
	if (l <= a[root].lt&&r >= a[root].rt) {
		a[root].c++;
		update(root);
		return;
	}
	int mid = (a[root].lt + a[root].rt) >> 1;
	if (l < mid) insert(l, r, root << 1);
	if (r > mid)insert(l, r, root << 1 | 1);
	update(root);
}
void delt(int l, int r, int root) {
	if (l <= a[root].lt&&r >= a[root].rt) {
		a[root].c--;
		update(root);
		return;
	}
	int mid = (a[root].lt + a[root].rt) >> 1;
	if (l < mid) delt(l, r, root << 1);
	if (r > mid) delt(l, r, root << 1 | 1);
	update(root);
}
int main() {
	int t = 0;
	double x1, y1, x2, y2;
	while (scanf("%d", &n)) {
		if (n == 0)break;
		for (int i = 0; i < n; i++) {
			scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
			L[i * 2].x = x1; L[i * 2].y1 = y1;
			L[i * 2].y2 = y2; L[i * 2].s = 1;
			L[i * 2 + 1].x = x2; L[i * 2 + 1].y1 = y1;
			L[i * 2 + 1].y2 = y2; L[i * 2 + 1].s = 0;
			Y[i * 2] = y1, Y[i * 2 + 1] = y2;
		}
		n <<= 1;
		sort(Y, Y + n);
		sort(L, L + n, cmp);
		re[1] = Y[0];
		int ans = 2;
		for (int i = 1; i < n; i++) {
			if (Y[i] != re[ans - 1]) {
				re[ans++] = Y[i];
			}
		}
		for (int i = 1; i < ans; i++)
			res[re[i]] = i;
		build(1, ans - 1, 1);
		double sum = 0;
		for (int i = 0; i < n - 1; i++) {
			int Lt = res[L[i].y1], Rt = res[L[i].y2];
			if (L[i].s) {
				insert(Lt, Rt, 1);
			}
			else {
				delt(Lt, Rt, 1);
			}
			sum += a[1].m*(L[i + 1].x - L[i].x);
		}
		printf("Test case #%d\n", ++t);
		printf("Total explored area: %.2lf\n", sum);
		printf("\n");
	}
	//while (1);
	return 0;
}

poj-1177 矩阵周长并

http://poj.org/problem?id=1177

线段树+扫描线

题解:https://blog.csdn.net/qq_42037034/article/details/80341858

矩形周长并,求轮廓周长
这题可以不离散化做的,我做的是离散化以后的

code:

#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define N 500005
struct node {
	int l, r, h, v;
	node() {}
	node(int l, int r, int h, int v):l(l),r(r),h(h),v(v){}
	bool operator < (const node s)const {
		return h < s.h;
	}
}a[N];
int M[N], Y[N], t[N];
int rm[N], lm[N], vb[N], sum[N];
mapq;
int n;
void build(int l, int r, int c) {
	rm[c] = lm[c] = vb[c] = sum[c] = t[c] = 0;
	if (l == r)return;
	int mid = (l + r) >> 1;
	build(l, mid, c << 1);
	build(mid + 1, r, c << 1 | 1);
}
void push_date(int l, int r, int c) {
	if (t[c]) {
		sum[c] = Y[r + 1] - Y[l];
		rm[c] = 1; lm[c] = 1;
		vb[c] = 2;
	}
	else {
		if (l == r) {
			sum[c] = 0;	rm[c] = lm[c] = 0;vb[c] = 0;
		}
		else {
			sum[c] = sum[c << 1] + sum[c << 1 | 1];
			rm[c] = rm[c << 1 | 1];
			lm[c] = lm[c << 1];
			vb[c] = vb[c << 1] + vb[c << 1 | 1];
			if (lm[c << 1 | 1] && rm[c << 1])vb[c] -= 2;
		}
	}
}
void insert(int l, int r,int lt,int rt, int v,int c) {
	if (l <= lt && r >= rt) {
		t[c] += v;
		push_date(lt, rt, c);
		return;
	}
	int mid = (lt + rt) >> 1;
	if (l <= mid)insert(l, r, lt, mid, v, c << 1);
	if (r > mid)insert(l, r, mid+1, rt, v, c << 1 | 1);
	push_date(lt, rt, c);
}
int main() {
	int x1, x2, y1, y2;
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
		a[i * 2] = node(x1, x2, y1, 1);
		a[i * 2 + 1] = node(x1, x2, y2, -1);
		M[i * 2] = x1, M[i * 2 + 1] = x2;
	}
	n <<= 1;
	sort(a, a + n);
	sort(M, M + n);
	int k = unique(M, M + n) - M;
	int cnt = 0;
	for (int i = 0; i < k; i++) {
		Y[++cnt] = M[i];
		q[M[i]] = cnt;
	}
	build(1, cnt - 1, 1);
	int C = 0, last = 0;
	for (int i = 0; i < n; i++) {
		int lt = q[a[i].l], rt = q[a[i].r];
		insert(lt, rt - 1, 1, cnt - 1, a[i].v, 1);
		C += vb[1] * (a[i + 1].h - a[i].h);
		C += abs(sum[1] - last);
		last = sum[1];
	}
	cout << C << endl;
	return 0;
}

扫描线总结:https://blog.csdn.net/acmdream/article/details/73301963

poj 1037 

http://poj.org/problem?id=1037

很水的一题,从开始遍历一遍输入的x坐标,求对于前n-1的x坐标小于第n个x坐标的个数。

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

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define N 15005
struct node {
	int x, y;
}a[N];
int b[32002<<2];
int n;
int vis[N];
int searchs(int l, int r, int c, int k) {
	if (k == r)return b[c];
	int mid = (l + r) >> 1;
	if (k <= mid) return searchs(l, mid, c << 1, k);
	else return b[c << 1] + searchs(mid + 1, r, c << 1 | 1, k);
}
void insert(int l, int r, int c, int k) {
	b[c] += 1;
	if (l == r)return;
	int mid = (l + r) >> 1;
	if (k <= mid) insert(l, mid, c << 1, k);
	else insert(mid + 1, r, c << 1 | 1, k);
}
int main() {
	memset(vis, 0, sizeof(vis));
	memset(b, 0, sizeof(b));
	int mx = 32002;
	scanf("%d", &n);
	int ans;
	for (int i = 0; i < n; i++) {
		scanf("%d%d", &a[i].x, &a[i].y);
		ans = searchs(1, mx, 1, a[i].x + 1);
		vis[ans]++;
		insert(1, mx, 1, a[i].x + 1);
	}
	for (int i = 0; i < n; i++)
		cout << vis[i] << endl;
	return 0;
}

 

你可能感兴趣的:(算法)