&st表
#include<cstdio> #include<iostream> #include<cstring> #include<cmath> using namespace std; int n,q,l,r,a[100005],f[100005][20]; int main(){ scanf("%d%d",&n,&q); for(int i = 1; i <= n; i++){ scanf("%d",&a[i]); f[i][0] = a[i]; } int p = log2(n); cout<<"p: "<<p<<endl; for(int j = 1; j <= p; j++){ //f[i][4] 到达 i + 2^4 - 1 for(int i = 1; i + (1<<j) - 1 <= n; i++){ f[i][j] = max(f[i][j-1], f[i + (1<<(j-1))][j-1]); } } for(int i = 1; i <= q; i++){ scanf("%d%d",&l,&r);// [l, l + 2^s - 1] 和 [r - 2^s +1, r] int j = log2(r - l + 1);// l + 2^s - 1 = r printf("%d\n",max(f[l][j],f[r - (1<<j) + 1][j])); } return 0; } /* 9 2 13 2 -6 10 9 -12 -4 2 19 1 8 5 6 */
P2880 [USACO07JAN]Balanced Lineup G
&数状数组
&线段树
1.单点修改,区间查询 luogu3374
#include <iostream> #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> #include <queue> // #define int long long #define ll long long using namespace std; const int mxn = 1e6 + 5; ll n, m; struct Tree { int l, r; ll sum, gcd; } tree[mxn * 4]; ll a[mxn], b[mxn]; void pushup(int rt) { tree[rt].sum = tree[rt << 1].sum + tree[rt << 1 | 1].sum; } void build(int rt, int l, int r) { tree[rt].l = l, tree[rt].r = r; if (l == r) { tree[rt].sum = b[l]; return; } int mid = (l + r) >> 1; build(rt << 1, l, mid); build(rt << 1 | 1, mid + 1, r); pushup(rt); } void update(int rt, int pos, ll val) { // if (tree[rt].l == tree[rt].r) { tree[rt].sum += val; return; } int mid = (tree[rt].l + tree[rt].r) >> 1; if (pos <= mid) update(rt << 1, pos, val); else update(rt << 1 | 1, pos, val); pushup(rt); } ll query(int rt, int l, int r) { if (l <= tree[rt].l && r >= tree[rt].r) return tree[rt].sum; int mid = (tree[rt].l + tree[rt].r) >> 1; ll sum = 0; if (l <= mid) sum += query(rt << 1, l, r); if (r > mid) sum += query(rt << 1 | 1, l, r); return sum; } signed main() { char op[5]; ll x, y, z; scanf("%lld", &n); for (int i = 1; i <= n; i++) { scanf("%lld", &b[i]); } build(1, 1, n); scanf("%lld", &m); while (m--) { scanf("%s%lld%lld", op, &x, &y); if (op[0] == 'A') { update(1, x, y); } else { ll t = query(1, x, y); printf("%lld\n", t); } } return 0; }
2.Acwing : internal gcd
gcd(x,y) = gcd(x,y-x)
gcd(x,y,z) = gcd(x,y-x,z-y)
数学归纳法证明多个变量也满足此规律
那么:对于某一段al到ar的gcd,即gcd(al, al+1, al+2 ... ... ar),就是在求gcd(al, al+1 - al, al +2 - al+1... ... ar - ar-1),
如果对于原来数组某一段+d1或者-d2,差分后a数组都会被减掉,那么我们可以维护差分序列的gcd,即gcd(al+1 - al, al +2 - al+1... ... ar - ar-1),在上面进行单点的+d1或者-d2,最后求出l位置的前缀和以及其他位置差分数组的gcd。
用线段树维护差分序列,x的值可以通过线段树求出差分的前缀和t = query_one(1, 1, x)。
那么x到y的gcd就是query(t, query_gcd(1, x+1, y))的值。
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #include<queue> // #define int long long #define ll long long using namespace std; const int mxn = 5e5 + 5; ll n,m; struct Tree{ int l,r; ll sum, gcd; }tree[mxn * 4]; ll a[mxn], b[mxn]; ll gcd(ll x, ll y){ return y == 0 ? x: gcd(y,x % y);// y == 0 ? y: gcd(y, x - y); } void pushup(int rt){ tree[rt].sum = tree[rt<<1].sum + tree[rt<<1|1].sum; tree[rt].gcd = gcd(tree[rt<<1].gcd, tree[rt<<1|1].gcd); } void build(int rt, int l, int r){ tree[rt].l = l, tree[rt].r = r; if(l == r){ tree[rt].sum = b[l]; tree[rt].gcd = b[l]; return; } int mid = (l + r) >>1; build(rt<<1, l, mid); build(rt<<1 | 1, mid+1, r); pushup(rt); } void update(int rt, int pos, ll val){// if(tree[rt].l == tree[rt].r){ tree[rt].sum += val, tree[rt].gcd += val; return; } int mid = (tree[rt].l + tree[rt].r) >> 1; if(pos <= mid) update(rt<<1, pos, val); else update(rt<<1 | 1, pos, val); pushup(rt); } ll query_one(int rt, int l, int r){ if(l <= tree[rt].l && r >= tree[rt].r) return tree[rt].sum; int mid = (tree[rt].l + tree[rt].r)>>1; ll sum = 0; if(l <= mid) sum += query_one(rt<<1, l, r); if(r > mid) sum += query_one(rt<<1|1, l , r); return sum; } ll query_gcd(int rt, int l, int r){ if(l <= tree[rt].l && r >= tree[rt].r) return tree[rt].gcd; int mid = (tree[rt].l + tree[rt].r)>>1; ll lg = 0 ,rg = 0;//0 and another's gcd is another if(l <= mid) lg = query_gcd(rt<<1, l, r); if(r > mid) rg = query_gcd(rt<<1|1, l, r); return gcd(lg,rg);//error gcd(l,r); } signed main(){ char op; ll x,y,z; scanf("%lld%lld",&n,&m); for(int i = 1; i <= n; i++){ scanf("%lld",&a[i]); b[i] = a[i] - a[i-1]; } build(1,1,n); while(m--){ scanf(" %c%lld%lld",&op,&x,&y); if(op == 'C'){ scanf("%lld",&z); update(1, x, z); if(y + 1 <= n) update(1, y+1, -z); }else{ ll t = query_one(1, 1, x); printf("%lld\n",abs(gcd(t, query_gcd(1, x+1, y)))); } } return 0; }
3.上帝造题的七分钟/花神游历各国
对于开根号,可以发现,10^6最多开6次就到1了,那么对于某一段区间如果最大值是1,说明无需修改,如果>1,那么暴力开根号。因此维护一个最大值,一个求和即可。
void change(int rt, int l, int r){
if(tree[rt].mx == 1) return;//当>1时暴力修改,当==1时不修改直接跳过
if(tree[rt].l == tree[rt].r){
tree[rt].sum = sqrt(tree[rt].sum);
tree[rt].mx = sqrt(tree[rt].mx);
return;
}
int mid = (tree[rt].l + tree[rt].r)>>1;
if(l <= mid)
change(rt<<1, l, r);//这个范围不能更改
if(r > mid)
change(rt<<1|1, l, r);
pushup(rt);
}
#include<bits/stdc++.h> #define int long long using namespace std; const int mxn = 100005; int n,m,a[mxn]; struct Tree{ int l, r, sum, mx; }tree[4*mxn]; void pushup(int rt){ tree[rt].sum = tree[rt<<1].sum + tree[rt<<1|1].sum; tree[rt].mx = max(tree[rt<<1].mx, tree[rt<<1|1].mx); } void build(int rt, int l, int r){ tree[rt].l = l, tree[rt].r = r; if(l == r){ tree[rt].sum = a[l]; tree[rt].mx = a[l]; return; } int mid = (l + r) >> 1; build(rt<<1, l, mid); build(rt<<1 | 1, mid+1, r); pushup(rt); } //正整数数列 void change(int rt, int l, int r){ if(tree[rt].mx == 1) return;//当>1时暴力修改,当==1时不修改直接跳过 if(tree[rt].l == tree[rt].r){ tree[rt].sum = sqrt(tree[rt].sum); tree[rt].mx = sqrt(tree[rt].mx); return; } int mid = (tree[rt].l + tree[rt].r)>>1; if(l <= mid) change(rt<<1, l, r);//这个范围不能更改 if(r > mid) change(rt<<1|1, l, r); pushup(rt); } int query(int rt, int l, int r){ if(l <= tree[rt].l && r >= tree[rt].r){ return tree[rt].sum; } int mid = (tree[rt].l + tree[rt].r)>>1; int sum = 0; if(l <= mid) sum += query(rt<<1, l, r); if(r > mid) sum += query(rt<<1|1, l, r); return sum; } void print(){ for(int i = 1; i<= 25; i++) cout<<"ceshi: "<<i<<" "<<tree[i].sum<<" "<<tree[i].mx<<endl; } signed main(){ scanf("%lld",&n); int x,y,op; for(int i = 1; i <= n; i++) scanf("%lld",&a[i]); build(1, 1, n); scanf("%lld",&m); for(int i = 1; i <= m; i++){ scanf("%lld%lld%lld",&op,&x,&y); if(x > y) swap(x,y); //忘了:注意l有可能大于r,遇到这种情况请交换l,r。 if(op == 0){ change(1, x, y); // print(); } else cout<<query(1, x, y)<<endl; } return 0; }
也可以用以下方法:
```
//如果这个区间的标记是0或者是1,那么这一段就都不用再修改了
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
int a[100005];
int x,l,r;
struct P {
int l,r;
int sum;
bool flag;
} tree[400050];
void pushup(int t) {
tree[t].sum =tree[t*2].sum +tree[t*2+1].sum ;
if(tree[t*2].flag and tree[t*2+1].flag )tree[t].flag=1;
return;
}
void build(int t,int l,int r) {
tree[t].l =l; tree[t].r =r;
if(l==r) {
tree[t].sum =a[l];
if(a[l]==1 or (not a[l]))tree[t].flag=1;
return;
}
int mid=(l+r)/2.0;
build(t*2,l,mid);
build(t*2+1,mid+1,r);
pushup(t);
return;
}
int qcheck(int t,int l,int r) {
if(l<=tree[t].l and tree[t].r <=r) {
return tree[t].sum ;
}
int mid=(tree[t].l +tree[t].r)/2.0;
int ans=0;
if(l<=mid)ans=qcheck(t*2,l,r);
if(r>mid)ans+=qcheck(t*2+1,l,r);
pushup(t);
return ans;
}
void cnote(int t,int l,int r) {
if(tree[t].flag )return;
if(tree[t].l ==tree[t].r ) {
tree[t].sum =(int)sqrt(tree[t].sum );
if(tree[t].sum == 1 or not(tree[t].sum ))tree[t].flag=1;
return;
}
int mid=(tree[t].l +tree[t].r )/2.0;
if(l<=mid)cnote(t*2,l,r);
if(r>mid) cnote(t*2+1,l,r);
pushup(t);
return;
}
signed main() {
ios::sync_with_stdio(0);
cin>>n;
for(int i=1; i<=n; ++i) {
cin>>a[i];
}
build(1,1,n);
cin>>m;
for(int i=1; i<=m; ++i) {
cin>>x>>l>>r;
if(l > r) swap(l,r);
if(x==0) {
cnote(1,l,r);
}else{
cout<<qcheck(1,l,r)<<endl;
}
}
return 0;
}
/*
4
1 100 5 5
2
2 1 2
1 1 2
输入11
*/
```
4.区间修改,单点查询
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #define LL long long #define int long long #define MAXN 1000005 using namespace std; int n, m; LL a[MAXN]; struct Node { int l, r; LL sm, ad; } tre[MAXN << 2]; inline void pushup(int rt) { tre[rt].sm = tre[rt << 1].sm + tre[rt << 1 | 1].sm; } inline void pushdown(int rt) { if (tre[rt].ad) { int tad = tre[rt].ad; tre[rt << 1].ad += tad; tre[rt << 1 | 1].ad += tad; tre[rt << 1].sm += tad * (tre[rt << 1].r - tre[rt << 1].l + 1); //(2)下放标记的同时,将该区间的sm值更新,以便于下次查询可以有sm的正确返回值 tre[rt << 1 | 1].sm += tad * (tre[rt << 1 | 1].r - tre[rt << 1 | 1].l + 1); tre[rt].ad = 0; } } inline void build(int rt, int L, int R) { tre[rt].l = L, tre[rt].r = R; if (L == R) { tre[rt].sm = a[L]; return; } int mid = (L + R) >> 1; build(rt << 1, L, mid); build(rt << 1 | 1, mid + 1, R); pushup(rt); } inline void update(int rt, int L, int R, int v) //区间修改 { if (L <= tre[rt].l && R >= tre[rt].r) { tre[rt].sm += (tre[rt].r - tre[rt].l + 1) * v; tre[rt].ad += v; return; } if (tre[rt].ad) pushdown(rt); //(1) int mid = (tre[rt].l + tre[rt].r) >> 1; if (L <= mid) update(rt << 1, L, R, v); if (R > mid) update(rt << 1 | 1, L, R, v); pushup(rt); } inline LL query(int rt, int pos) { if (tre[rt].l == tre[rt].r) { return tre[rt].sm; } if (tre[rt].ad) pushdown(rt); int mid = (tre[rt].l + tre[rt].r) >> 1; if (pos <= mid) return query(rt << 1, pos); //少+ else return query(rt << 1 | 1, pos); } signed main() { scanf("%lld%lld", &n, &m); for (register int i = 1; i <= n; i++) { scanf("%lld", &a[i]); } build(1, 1, n); // for(int i=1;i<=9;i++)cout<<tre[i].sm<<" "<<tre[i].l<<" "<<tre[i].r<<endl; int q, x, y; LL k; int pos; for (register int i = 1; i <= m; i++) { scanf("%lld", &q); if (q == 2) { scanf("%lld", &pos); printf("%lld\n", query(1, pos)); } else { scanf("%lld%lld%lld", &x, &y, &k); if (k == 0) continue; update(1, x, y, k); } } }
5.区间修改,区间查询
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #define MAXN 1000005 #define LL long long #define int long long using namespace std; int n, m; LL a[MAXN]; struct Node { int l, r; LL sm, ad; } tre[MAXN << 2]; void pushup(int rt) { tre[rt].sm = tre[rt << 1].sm + tre[rt << 1 | 1].sm; } void pushdown(int rt) { if (tre[rt].ad) { int tad = tre[rt].ad; tre[rt << 1].ad += tad; tre[rt << 1 | 1].ad += tad; tre[rt << 1].sm += tad * (tre[rt << 1].r - tre[rt << 1].l + 1); //(2)下放标记的同时,将该区间的sm值更新,以便于下次查询可以有sm的正确返回值 tre[rt << 1 | 1].sm += tad * (tre[rt << 1 | 1].r - tre[rt << 1 | 1].l + 1); tre[rt].ad = 0; } } void build(int rt, int L, int R) { tre[rt].l = L, tre[rt].r = R; if (L == R) { tre[rt].sm = a[L]; return; } int mid = (L + R) >> 1; build(rt << 1, L, mid); build(rt << 1 | 1, mid + 1, R); pushup(rt); } void update(int rt, int L, int R, int v) //区间修改 { if (L <= tre[rt].l && R >= tre[rt].r) { tre[rt].sm += (tre[rt].r - tre[rt].l + 1) * v; tre[rt].ad += v; return; } pushdown(rt); //(1) int mid = (tre[rt].l + tre[rt].r) >> 1; if (L <= mid) update(rt << 1, L, R, v); if (R > mid) update(rt << 1 | 1, L, R, v); pushup(rt); } LL query(int rt, int L, int R) { if (L <= tre[rt].l && R >= tre[rt].r) { return tre[rt].sm; } pushdown(rt); int mid = (tre[rt].l + tre[rt].r) >> 1; LL ans = 0; if (L <= mid) ans += query(rt << 1, L, R); //少+ if (R > mid) ans += query(rt << 1 | 1, L, R); return ans; } signed main() { scanf("%lld%lld", &n, &m); for (int i = 1; i <= n; i++) { scanf("%lld", &a[i]); } build(1, 1, n); // for(int i=1;i<=9;i++)cout<<tre[i].sm<<" "<<tre[i].l<<" "<<tre[i].r<<endl; int q, x, y; LL k; for (int i = 1; i <= m; i++) { scanf("%lld", &q); if (q == 2) { scanf("%lld%lld", &x, &y); printf("%lld\n", query(1, x, y)); } else { scanf("%lld%lld%lld", &x, &y, &k); update(1, x, y, k); } } }
6线段树2(区间加乘,区间查询)
#include <iostream> #include <cstdio> #include <cmath> #include <cstring> #define mxn 100005 #define int long long using namespace std; int n, m, p, a[mxn], v, x, y, k; struct Tree { int l, r, mul, ad, rt, sum, len; } tree[mxn << 2]; void pushup(int rt) { tree[rt].sum = (tree[rt << 1].sum + tree[rt << 1 | 1].sum) % p; } void pushdown(int rt) { int tad = tree[rt].ad; int tml = tree[rt].mul; tree[rt].ad = 0; tree[rt].mul = 1; tree[rt << 1].ad = (tree[rt << 1].ad * tml + tad) % p; // p tree[rt << 1 | 1].ad = (tree[rt << 1 | 1].ad * tml + tad) % p; // p tree[rt << 1].mul = (tree[rt << 1].mul * tml) % p; tree[rt << 1 | 1].mul = (tree[rt << 1 | 1].mul * tml) % p; tree[rt << 1].sum = (tree[rt << 1].sum * tml + tad * tree[rt << 1].len) % p; tree[rt << 1 | 1].sum = (tree[rt << 1 | 1].sum * tml + tad * tree[rt << 1 | 1].len) % p; } //((x * mul) + ad) * mul' + ad' = x * mul * mul' + ad * mul' + ad' // sum: sigma((x * mul) + ad) void build(int l, int r, int rt) { tree[rt].l = l; tree[rt].r = r; tree[rt].mul = 1; tree[rt].ad = 0; tree[rt].len = r - l + 1; if (l == r) { tree[rt].sum = a[l] % p; return; } int mid = (r + l) >> 1; build(l, mid, rt << 1); build(mid + 1, r, (rt << 1) | 1); pushup(rt); } void update_ad(int l, int r, int rt, int ad) { //((x * mul) + ad) + ad' if (l <= tree[rt].l && r >= tree[rt].r) { tree[rt].ad = (tree[rt].ad + ad) % p; tree[rt].sum = (tree[rt].sum + ad * tree[rt].len) % p; return; } if (l > tree[rt].r || r < tree[rt].l) return; pushdown(rt); update_ad(l, r, rt << 1, ad); update_ad(l, r, rt << 1 | 1, ad); pushup(rt); } int query(int l, int r, int rt) { // cout<<l<<" "<<r<<" "<<rt<<" "<<tree[rt].l<<" "<<tree[rt].r<<endl; if (l <= tree[rt].l && r >= tree[rt].r) { return tree[rt].sum; } if (r < tree[rt].l || l > tree[rt].r) return 0; pushdown(rt); int tmp = 0; tmp += query(l, r, rt << 1) % p; tmp += query(l, r, rt << 1 | 1) % p; return tmp % p; } //((x * mul) + add) * mul' = x * mul * mul' + add * mul' void update_mul(int l, int r, int rt, int mul) { if (l <= tree[rt].l && r >= tree[rt].r) { tree[rt].mul = (tree[rt].mul * mul) % p; tree[rt].ad = (tree[rt].ad * mul) % p; tree[rt].sum = (tree[rt].sum * mul) % p; return; } if (r < tree[rt].l || l > tree[rt].r) return; pushdown(rt); update_mul(l, r, rt << 1, mul); update_mul(l, r, rt << 1 | 1, mul); pushup(rt); } signed main() { scanf("%lld%lld", &n, &p); for (int i = 1; i <= n; i++) scanf("%lld", &a[i]); build(1, n, 1); // for(int i = 1; i <= 2*n ; i++) // cout<<i<<" "<<tree[i].sum<<endl; scanf("%lld", &m); for (int i = 1; i <= m; i++) { scanf("%lld", &v); if (v == 1) { // mul process - ad = ad * mul , mul = mul scanf("%lld%lld%lld", &x, &y, &k); update_mul(x, y, 1, k); } else if (v == 2) { // add process - mul no change , ad + ad' scanf("%lld%lld%lld", &x, &y, &k); update_ad(x, y, 1, k); // for(int i = 1; i <= 2*n ; i++) // cout<<i<<" "<<tree[i].sum<<endl; } else if (v == 3) { scanf("%lld%lld", &x, &y); printf("%lld\n", query(x, y, 1)); } } return 0; }
小白逛公园
P5490 扫描线
1.如图所示,进入矩形线段树值+1,出了矩形线段树值-1。
2.由于数据比较大,所以需要离散化。但是离散化以后,一个点不能代表点,而代表相邻的两个位置。比如样例:
100 150 200 255
离散化以后是 1 2 3 4
但是修改操作是100 - 200的线段树+1,
此时对于离散化后的是1-2的线段树+1,这样实际加的是100-150,250-200.
如果不是这样的话,会丢失长度。比如修改1,2,再修改3,那么总的线段树长度是1-2加上3,3是一个点就为0,那么2-3之间的距离丢失。
对于懒标记,可以不加的原因是因为区间加减成对出现。
#include<bits/stdc++.h> #define int long long using namespace std; int n,b[1000005]; struct Tree{ int l,r,val,len; }tree[4000005]; struct C{ int x,ya,yb,f; bool operator < (const C p)const{ return x < p.x; } }c[1000005]; void pushup(int rt){ if(tree[rt].val) tree[rt].len = b[tree[rt].r+1] - b[tree[rt].l]; else tree[rt].len = tree[rt<<1].len + tree[rt<<1|1].len; } void build(int rt, int l, int r){ tree[rt].l = l; tree[rt].r = r; if(l == r){ tree[rt].val = 0;// return; } int mid = (l + r) >> 1; build(rt<<1, l, mid); build(rt<<1|1, mid+1, r); // tree[rt].len = tree[rt<<1].len + tree[rt<<1|1].len; } void change(int rt, int l, int r, int val){//tree[1].len if(l <= tree[rt].l && r >= tree[rt].r){ tree[rt].val += val; pushup(rt); return ; } int mid = (tree[rt].l + tree[rt].r) >> 1; if(l <= mid) change(rt<<1, l, r, val); if(r > mid) change(rt<<1|1, l, r, val); pushup(rt); } signed main(){ scanf("%lld",&n); int cnt = 0, xa, ya, xb, yb; for(int i = 1; i <= n; i++){ scanf("%lld%lld%lld%lld",&xa, &ya, &xb, &yb); b[++cnt] = ya; c[cnt].f = 1, c[cnt].x = xa, c[cnt].ya = ya, c[cnt].yb = yb; b[++cnt] = yb; c[cnt].f = -1, c[cnt].x = xb, c[cnt].ya = ya, c[cnt].yb = yb; } sort(c+1, c+cnt+1); sort(b+1, b+cnt+1); int m = unique(b+1, b+cnt+1) - (b+1); build(1,1,m-1); int x,y,area = 0; for(int i = 1; i <= cnt; i++){ x = lower_bound(b+1, b+m+1, c[i].ya) - b; y = lower_bound(b+1, b+m+1, c[i].yb) - b - 1; area += tree[1].len * (c[i].x - c[i-1].x); // cout<<tree[1].len<<endl; change(1, x, y, c[i].f); } cout<<area<<endl; return 0; }