线段树---点修改
题目大意:
有编号是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;
}