The Fifth Week
耐心和恒心总会得到报酬的。 ————爱因斯坦
一、前言
周一组队赛,打得上海ccpc,赛后看了当时的榜,换算一下稳银了yeah,打得非常好,赛后把他们做的题也补了一下。
周二晚上的cf没有打,周三早上打的,就那样吧。
周三下午河南萌新联赛,过了五题,感觉算法学得还是不太行,考点算法就原形毕露了。
周六下午组队赛,打得有点着急忘记看榜了,迄今为止也看不了…服了朋友。反正ac俩题速度挺快的。
二、算法
1.线段树
//单点修改,区间查询
int a[100000];
int n;
int tree[4*1000];
void build (int p ,int l, int r) {
//编号,左右区间
if(l == r) {
tree[p] = a[l];
//只剩一个数字
return ;
}
int mid = (l+r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
tree[p] = tree[p*2] + tree[p*2+1];
//节点所代表的区间和
}
void change(int p,int l,int r, int pos, int num) {
if(l == r) {
tree[p] += num;
return ;
}
int mid = (l+r)/2;
if(pos <= mid) {
//只在左子树,就往它里面修改
change(p*2,l,mid,pos,num);
}
else change(p*2+1,mid+1,r,pos,num);
//只在右子树,就在它里面修改
tree[p] = tree[p*2] + tree[p*2+1];
//修改后再次更新区间和的值
}
int calc(int p,int l,int r, int x, int y) {
if(x <= l && r <= y) {
//完整包含在区间内,直接返回和
return tree[p];
}
int mid = (l+r)/2;
if(y <= mid) return calc(p*2,l,mid,x,y);
//完整的在这个区间左边
if(x > mid) return calc(p*2+1,mid+1,r,x,y);
//完整的在这个区间右边
return calc(p*2,l,mid,x,mid) + calc(p*2+1,mid+1,r,mid+1,y);
}
signed main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
build(1,1,n);
//1号节点代表的是1到n
for (int i = 1; i <= n; i++) {
int x,y,z;
cin >> x >> y >> z;
if(x == 1) {
change(1,1,n,y,z);
//第y个位置的数字加上z
}
else {
cout << calc(1,1,n,y,z) << endl;
//查询区间y到z的和
}
}
return 0;
}
//区间修改,区间查询
int a[100000];
int n;
int tree[4*1000];
//这个点的区间和
void build (int p ,int l, int r) {
//编号,左右区间
if(l == r) {
tree[p] = a[l];
//只剩一个数字
return ;
}
int mid = (l+r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
tree[p] = tree[p*2] + tree[p*2+1];
//节点所代表的区间和
}
int lazy[100000];
void pushdown(int p,int l,int r) {
int mid = (r+l)/2;
lazy[p*2] += lazy[p];
lazy[p*2+1] += lazy[p];
//俩个儿子的lazy赋值
tree[p*2] += lazy[p] * (mid-l+1);
tree[p*2+1] += lazy[p] * (r-(mid+1)+1);
//更改俩个儿子区间和
lazy[p] = 0;
//俩个儿子已经知道了,我就不用了
}
void change(int p,int l,int r, int x,int y,int num) {
if(x <= l && r <= y) {
tree[p] += num*(r-l+1);
lazy[p] += num;
return ;
}
if(lazy[p] != 0) {
pushdown(p,l,r);
}
int mid = (l+r)/2;
if(x <= mid) change(p*2,l,mid,x,y,num);
if(y > mid) change(p*2+1,mid+1,r,x,y,num);
tree[p] = tree[p*2] + tree[p*2+1];
//修改后再次更新区间和的值
}
int calc(int p,int l,int r, int x, int y) {
if(x <= l && r <= y) {
return tree[p];
}
if(lazy[p] != 0) {
pushdown(p,l,r);
//把标记往下传
}
int mid = (l+r)/2;
// if(y <= mid) return calc(p*2,l,mid,x,y);
// //完整的在这个区间左边
// if(x > mid) return calc(p*2+1,mid+1,r,x,y);
// //完整的在这个区间右边
// return calc(p*2,l,mid,x,mid) + calc(p*2+1,mid+1,r,mid+1,y);
int ans = 0;
if(x <= mid) ans += calc(p*2,l,mid,x,y);
if(y > mid) ans += calc(p*2+1,mid+1,r,x,y);
return ans;
}
signed main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
build(1,1,n);
//1号节点代表的是1到n
for (int i = 1; i <= n; i++) {
int x,y,z;
char op;
cin >> op;
if(op == 'C') {
cin >> x >> y >> z;
change(1,1,n,x,y,z);
//第y个位置的数字加上z
}
else {
cin >> y >> z;
cout << calc(1,1,n,y,z) << endl;
//查询区间y到z的和
}
}
return 0;
}
<1>([JSOI2008]最大数MAXNUMBER)
题解:
维护一个数组,Q L表示查询末尾L个数中的最大值,A n表示将n加上最近一次查询操作的答案对d取模后插入数列末尾。
线段树的思维,因为最多有m个节点,所以可以直接建一颗有m个节点的树,然后进行修改操作即可。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int m,d;
const int N = 2e5+5;
vector<int>a;
int tree[4*N];
void build(int p,int l,int r) {
if(l == r) {
tree[p] = 0;
return ;
}
int mid = (l+r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
tree[p] = max(tree[p*2],tree[p*2+1]);
}
void add(int p,int l,int r,int pos,int num) {
if(l == r) {
tree[p] = num;
return ;
}
int mid = (l+r)/2;
if(pos <= mid) add(p*2,l,mid,pos,num);
else add(p*2+1,mid+1,r,pos,num);
tree[p] = max(tree[p*2],tree[p*2+1]);
}
int calc(int p,int l,int r,int x,int y) {
if(x <= l && y <= r) return tree[p];
int mid = (l+r)/2;
if(y <= mid) return calc(p*2,l,mid,x,y);
if(x > mid) return calc(p*2+1,mid+1,r,x,y);
return max(calc(p*2,l,mid,x,mid),calc(p*2+1,mid+1,r,mid+1,y));
}
signed main() {
cin >> m >> d;
int p = 0;
int t = 0;
build(1,1,m);
for (int i = 1; i <= m; i++) {
char op;
cin >> op;
int x; cin >> x;
if(op == 'A') {
p++;
x = x+t;
x = x%d;
add(1,1,m,p,x);
}
else {
t = calc(1,1,m,p-x+1,p)%d;
cout << t << endl;
}
}
return 0;
}