openjudge上面cdqz的数据结构22题非常良心啊!!!(虽然太菜了只做了几道。。
然后对线段树有了一些更多的理解
(为什么1004提交不起aaaa很想知道有没有把指针写对qwq
1002:Challenge 2
查看 提交 统计 提问
总时间限制: 10000ms 单个测试点时间限制: 1000ms 内存限制: 262144kB
描述
给一个空数列,有M次操作,每次操作是以下三种之一:
(1)在数列后加一个数
(2)求数列中某位置的值
(3)撤销掉最后进行的若干次操作(1和3)
输入
第一行一个正整数M。
接下来M行,每行开头是一个字符,若该字符为’A’,则表示一个加数操作,接下来一个整数x,表示在数列后加一个整数x;若该字符为’Q’,则表示一个询问操作,接下来一个整数x,表示求x位置的值;若该字符为’U’,则表示一个撤销操作,接下来一个整数x,表示撤销掉最后进行的若干次操作。
输出
对每一个询问操作单独输出一行,表示答案。
样例输入
9
A 1
A 2
A 3
Q 3
U 1
A 4
Q 3
U 2
Q 3
样例输出
3
4
3
提示
1<=M<=10^5,输入保证合法,且所有整数可用带符号32位整型存储。
带撤销操作,直接主席树动态开点,把每个操作的根节点存下来,撤销时返回就可以。【注意】根节点点数是有效操作决定的,询问不计入。
#include<iostream>
#include<cstdio>
using namespace std;
struct node {
int num;
node *ls, *rs;
} *zero, pool[100005*32], *tail = pool, *root[100005];
int len[100005];
node * newnode ( ) {
node * nd = ++ tail;
nd -> ls = zero;
nd -> rs = zero;
nd -> num = 0;
return nd;
}
node * build ( ) {
node * nd = newnode ( );
return nd;
}
node * add ( node * nd, int l, int r, int pos, int a ) {
node * nnd = newnode ( );
if ( l == r ) {
nnd -> num = a;
return nnd;
}
int mid = ( l + r ) >> 1;
if ( pos <= mid ) {
nnd -> rs = nd -> rs;
nnd -> ls = add ( nd -> ls, l, mid, pos, a );
} else {
nnd -> ls = nd -> ls;
nnd -> rs = add ( nd -> rs, mid + 1, r, pos, a );
}
return nnd;
}
int query ( node * nd, int l, int r, int pos ) {
if ( l == r ) return nd -> num;
int mid = ( l + r ) >> 1;
if ( pos <= mid ) return query ( nd -> ls, l, mid, pos );
else return query ( nd -> rs, mid + 1, r, pos );
}
int main ( ) {
int m;
scanf ( "%d", &m );
zero = ++ tail;
zero -> ls = zero;
zero -> rs = zero;
zero -> num = 0;
int num = 0;
root[0] = build ( );
for ( int i = 1; i <= m; i ++ ) {
char opt; int a;
scanf ( "\n%c %d", &opt, &a );
if ( opt == 'A' ) {
root[num+1] = add ( root[num], 1, m, len[num] + 1, a );//////////线段树区间范围始终是大范围,不能只是一个版本的树的区间
len[num+1] = len[num] + 1;
num ++;
} else if ( opt == 'U' ) {
root[num+1] = root[num-a];
len[num+1] = len[num-a];
num ++;
} else {
printf ( "%d\n", query ( root[num], 1,m, a ) );/////////////
}
}
return 0;
}
1006:Challenge 6
查看 提交 统计 提问
总时间限制: 10000ms 单个测试点时间限制: 1000ms 内存限制: 262144kB
描述
给一个长为N的数列,有M次操作,每次操作是以下两种之一:
(1)修改数列中的一个数
(2)求数列中有多少个数比它前面的数都大
输入
第一行两个正整数N和M。
第二行N的整数表示这个数列。
接下来M行,每行开头是一个字符,若该字符为’M’,则表示一个修改操作,接下来两个整数x和y,表示把x位置的值修改为y;若该字符为’Q’,则表示一个询问操作,求数列中有多少个数比它前面的数都大。
输出
对每一个询问操作单独输出一行,表示答案。
样例输入
5 3
1 2 3 4 5
Q
M 2 4
Q
样例输出
5
3
提示
1<=N<=10^5,1<=M<=10^5,输入保证合法,且所有整数可用带符号32位整型存储。
这里的更新操作非常奇妙丫,看了本校学长一道相似题的题解(bzoj2957)有所感悟,每次更新往右子树内递归计算贡献。右子树还要分左段和右段判断是否左子树做出了贡献,然后继续递归下去。
#include<iostream>
#include<cstdio>
using namespace std;
struct node {
int val, num;
} TR[400005];
int n, m, a[100005];
int count ( int nd, int l, int r, int val ) {
if ( l == r ) return TR[nd].val > val;
int mid = ( l + r ) >> 1;
if ( TR[nd<<1].val <= val ) return count ( nd << 1 | 1, mid + 1, r, val );//如果右子树的左段小于左子树val则没有对左子树做出贡献,去递归右段对左子树的贡献
else return TR[nd].num - TR[nd<<1].num + count ( nd << 1, l, mid, val );///如果有贡献则右段贡献右子树总num-左段num(不能直接写右段num,那是在它内部产生的,没有包含对整个右子树的影响),递归计算左段对左子树贡献
}
void modify ( int nd, int l, int r, int pos, int d ) {
if ( l == r ) {
TR[nd].val = d;
TR[nd].num = 1;
return ;
}
int mid = ( l + r ) >> 1;
if ( pos <= mid ) modify ( nd << 1, l, mid, pos, d );
else modify ( nd << 1 | 1, mid + 1, r, pos, d );
TR[nd].val = max ( TR[nd<<1].val, TR[nd<<1|1].val );
TR[nd].num = TR[nd<<1].num + count ( nd << 1 | 1, mid + 1, r, TR[nd<<1].val );//进入右子树递归
}
int main ( ) {
scanf ( "%d%d", &n, &m );
for ( int i = 1; i <= n; i ++ ) {
scanf ( "%d", &a[i] );
modify ( 1, 1, n, i, a[i] );
}
for ( int i = 1; i <= m; i ++ ) {
char opt;
scanf ( "\n%c", &opt );
if ( opt == 'Q' ) printf ( "%d\n", TR[1].num );
else {
int pos, d;
scanf ( "%d%d", &pos, &d );
modify ( 1, 1, n, pos, d );
}
}
}
(还是先挂一个毕竟过了样例
1004:Challenge 4
查看 提交 统计 提问
总时间限制: 10000ms 单个测试点时间限制: 1000ms 内存限制: 262144kB
描述
给一个长为N的数列,有M次操作,每次操作时以下三种之一:
(1)修改数列中的一个数
(2)求数列中某连续一段所有数的两两乘积的和 mod 1000000007
(3)求数列中某连续一段所有相邻两数乘积的和 mod 1000000007
输入
第一行两个正整数N和M。
第二行N的整数表示这个数列。
接下来M行,每行开头是一个字符,若该字符为’M’,则表示一个修改操作,接下来两个整数x和y,表示把x位置的值修改为y;若该字符为’Q’,则表示一个询问操作,接下来两个整数x和y,表示对[x,y]区间做2号询问;若该字符为’A’,则表示一个询问操作,接下来两个整数x和y,表示对[x,y]区间做3号询问。
输出
对每一个询问操作单独输出一行,表示答案。
样例输入
5 5
1 2 3 4 5
Q 1 5
A 1 5
M 2 7
Q 1 5
A 1 5
样例输出
85
40
150
60
提示
1<=N<=10^5,1<=M<=10^5,输入保证合法,且所有整数可用带符号32位整型存储。
2的更新方法推一下就可以出来,但是查询时需要左子树的sum右子树的sum以及他们各自的答案,用结构体把它们一起存下来返回就可以叻。
更新!!结果是指针池开小叻qwq,可能还有mod的问题。以后可能会转博客园叻突然有点舍不得qwq,就是最后一篇blog了吧。
#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
const int mod = 1000000007;
const int N = 100005;
ll a[N];
int n, m;
struct node {
node *ls, *rs;
ll sum, num1, num2, lf, rg;
} pool[N*32], *tail = pool, *zero, *root;
void update ( node *nd, node *ld, node *rd ) {
nd -> sum = ( ld -> sum + rd -> sum + mod ) % mod;
nd -> num1 = ( ld -> sum * rd -> sum % mod + ld -> num1 + rd -> num1 + mod ) % mod;
nd -> num2 = ( ld -> num2 + rd -> num2 + ld -> rg * rd -> lf % mod + mod ) % mod;
nd -> lf = ld -> lf % mod; nd -> rg = rd -> rg % mod;
}
node *build ( int l, int r ) {
node *nd = ++ tail;
if ( l == r ) {
ll pp = ( a[l] % mod + mod ) % mod;
nd -> lf = nd -> rg = pp;
nd -> sum = pp; nd -> num1 = nd -> num2 = 0;
return nd;
}
int mid = ( l + r ) >> 1;
nd -> ls = build ( l, mid );
nd -> rs = build ( mid + 1, r );
update ( nd, nd -> ls, nd -> rs );
return nd;
}
node *query ( node *nd, int l, int r, int L, int R ) {
if ( l >= L && r <= R )
return nd;
int mid = ( l + r ) >> 1;
node *nd1 = ++ tail, *nd2 = ++ tail;
if ( mid >= L )
nd1 = query ( nd -> ls, l, mid, L, R );
if ( mid < R )
nd2 = query ( nd -> rs, mid + 1, r, L, R );
node *st = ++ tail;
update ( st, nd1, nd2 );
return st;
}
void modify ( node *nd, int l, int r, int pos, ll d ) {
if ( l == r ) {
nd -> lf = nd -> rg = d;
nd -> sum = d;
return ;
}
int mid = ( l + r ) >> 1;
if ( pos <= mid ) modify ( nd -> ls, l, mid, pos, d );
else modify ( nd -> rs, mid + 1, r, pos, d );
update ( nd, nd -> ls, nd -> rs );
}
int main ( ) {
scanf ( "%d%d", &n, &m );
for ( int i = 1; i <= n; i ++ )
scanf ( "%lld", &a[i] );
root = build ( 1, n );
for ( int i = 1; i <= m; i ++ ) {
char opt;
scanf ( "\n%c", &opt );
if ( opt == 'Q' ) {
int l, r;
scanf ( "%d%d", &l, &r );
node *ans = query ( root, 1, n, l, r );
printf ( "%lld\n", ( ans -> num1 + mod ) % mod );
} else if ( opt == 'A' ) {
int l, r;
scanf ( "%d%d", &l, &r );
node *ans = query ( root, 1, n, l, r );
printf ( "%lld\n", ( ans -> num2 + mod ) % mod );
} else {
int pos; ll r;
scanf ( "%d%lld", &pos, &r );
r = ( r + mod ) % mod;
modify ( root, 1, n, pos, r );
}
}
return 0;
}
后面还有好多道aaaaqwq,慢慢刷!