【线段树】poj 2182 “Lost Cows“ --- (点修改) / poj 3468 “A Simple Problem with Integers“ -- (区间修改)

线段树---点修改

题目大意:
有编号是1~n的n个数字,2<=n>=8000,乱序排列,顺序是未知的。对于每个位置的数字,知道排在它前面比它小的数字有多少个。求这个乱序数列的顺序。

输入样例:
5
1
2
1
0

输出样例:
2
4
5
3
1

题目链接

 

暴力法解题代码

#include <cstdio>
using namespace std;
const int maxn = 8005;

int main()
{
	int n;
	int pre[maxn], ans[maxn], num[maxn];
	scanf("%d",&n);
	for(int i = 1; i <= n; i++) num[i] = i;
	pre[1] = 0;
	for(int i = 2; i <= n; i++) scanf("%d",&pre[i]);
	
	for(int i = n; i >= 1; i--) { //从后往前处理数列
		int k = 0;
		for(int j = 1; j <= n; j++) { //查找num[]中未处理的第pre[i]+1大的数
			if (num[j] != -1) {
				k++;
				if (k == pre[i] + 1) { //找到了
					ans[i] = num[j]; //num[]中剩下的第pre[i]+1个数就是ans[i]
					num[j] = -1;
					break;
				}
			}
		}
	}
	
	for(int i = 1; i <= n; i++) printf("%d\n", ans[i]);
	
	return 0;
}

 

用结构体实现线段树的解题代码

#include <cstdio>
using namespace std;
const int maxn = 10010;
struct Tree {
	int l, r, len; //用len存储这个区间的数字个数,即这个结点下牛的数量 
}tree[4*maxn];
int pre[maxn], ans[maxn]; 

void BuildTree(int left, int right, int u) { //建树
	tree[u].l = left;
	tree[u].r = right;
	tree[u].len = right - left + 1; //更新结点u的值 
	
	if (left == right) return;
	BuildTree(left, (left+right)>>1, u<<1); //递归左子树 
	BuildTree(((left+right)>>1)+1, right, (u<<1)+1); //递归右子树
}

int query(int u, int num) { //查询+维护,所求值为当前区间左起第num个元素
	tree[u].len--; //对访问到的区间维护len,即把这个结点上牛的数量减一
	if (tree[u].l == tree[u].r) return tree[u].l;
	
	//情况一:左子区间内牛的个数不够,则查询右子区间中左起第num-tree[u<<1].len个元素
	if (tree[u<<1].len < num) return query((u<<1)+1, num-tree[u<<1].len);
	
	//情况二:左子区间内牛的个数足够,依旧查询左子区间中左起第num个元素
	if (tree[u<<1].len >= num) return query(u<<1, num);
}

int main()
{
	int n;
	scanf("%d",&n);
	pre[1] = 0;
	for(int i = 2; i <= n; i++) scanf("%d",&pre[i]);
	BuildTree(1, n, 1);
	for(int i = n; i >= 1; i--) ans[i] = query(1, pre[i]+1); //从后往前推断出每次最后一个数字
	
	for(int i = 1; i <= n; i++) printf("%d\n", ans[i]);
	
	return 0;
}
	 


 


 

 

线段树---区间修改

题目大意:
给出N个数,进行Q个操作,1 <= N,Q<= 100000。有两种操作:
“C a b c”,对区间[a, b]的每个数字加c;
“Q a b”,查询区间[a, b]的数字和。

输入:
N,Q,以及N个数字,Q个操作;

输出:
对每个查询操作,输出结果。

输入样例:
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

输出样例:4
55
9
15

题目链接

 

线段树---lazy-tag解题代码

#include <cstdio>
#include <climits>
using namespace std;
const int maxn = 1e5 + 10;
typedef long long ll;
struct Tree {
	ll l, r;
	ll sum, add;
}tree[maxn<<2]; //4倍空间

void push_up(int rt) { //向上更新,通过当前结点rt把值递归到父结点
	tree[rt].sum = tree[rt<<1].sum + tree[rt<<1 | 1].sum;
}

void push_down(int rt, int m) { //向下更新,更新rt的子结点
	if (tree[rt].add) {
		tree[rt<<1].add += tree[rt].add;
		tree[rt<<1 | 1].add += tree[rt].add;
		
		tree[rt<<1].sum += (m - (m>>1)) * tree[rt].add;
		tree[rt<<1 | 1].sum += (m>>1) * tree[rt].add;
		
		tree[rt].add = 0; //取消本层标记
	}
}

void build(int l, int r, int rt) { 
	tree[rt].add = 0;
	tree[rt].l = l;
	tree[rt].r = r;
	if (l == r) {
		scanf("%lld",&tree[rt].sum);
		return;
	}
	
	int mid = (l+r)>>1;
	build(l, mid, rt<<1);
	build(mid+1, r, rt<<1 | 1);
	push_up(rt);
}

void update(int a, int b, ll c, int l, int r, int rt) { //区间更新
	if (l >= a && r <= b) {
		tree[rt].sum += (r-l+1) * c;
		tree[rt].add += c;
		return;
	}
	
	push_down(rt, r-l+1); //向下更新
	int mid = (l+r) >> 1;
	/*分成两半,继续深入*/ 
	if (a <= mid) update(a, b, c, l, mid, rt<<1); 
	if (b > mid) update(a, b, c, mid+1, r, rt<<1 | 1);
	push_up(rt); //向上更新
}

ll query(int a, int b, int l, int r, int rt) { //区间求和
	if (l >= a && r <= b) return tree[rt].sum; 
	
	push_down(rt, r-l+1); 
	int mid = (l+r) >> 1;
	ll ans = 0;
	if (a <= mid) ans += query(a, b, l, mid, rt<<1);
	if (b > mid) ans += query(a, b, mid+1, r, rt<<1 | 1);
	
	return ans;
}

int main()
{
	int n, m;
	scanf("%d %d",&n,&m);
	build(1, n, 1);
	while (m--) {
		char str[2];
		int a, b;
		ll c;
		scanf("%s",str);
		if (str[0] == 'C') {
			scanf("%d %d %lld",&a,&b,&c);
			update(a, b, c, 1, n, 1);
		}
		else {
			scanf("%d %d",&a,&b);
			printf("%lld\n", query(a, b, 1, n, 1));
		}
	}
	
	return 0;
}
	

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

重剑DS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值