第一题:二维树状数组,bit[x][y]表示从(1,1)到(x,y)的和,二维的坐标从一维的推过来,正确性可以用一个递增和一个递减的序列单调性证明,至于构图就当黑箱吧
#include <cstdio> int n, m, q; struct Case1 { int bit[100010]; void modify( int p, int d ) { for( int i = p; i <= m; i += i & -i ) bit[i] += d; } int query( int r ) { int rt = 0; for( int i = r; i; i -= i & -i ) rt += bit[i]; return rt; } int query( int l, int r ) { return query(r) - query(l-1); } void solve() { while (q--) { char ss[100]; scanf( "%s", ss ); if( ss[0] == ‘m‘ ) { int x, y, d; scanf( "%d%d%d", &x, &y, &d ); modify( y, d ); } else { int x1, y1, x2, y2; scanf( "%d%d%d%d", &x1, &y1, &x2, &y2 ); printf( "%d\\n", query(y1,y2) ); } } } }case1;//一维 struct Case2 { int bit[1100][1100]; void modify( int x, int y, int d ) { for( int i = x; i <= n; i += i & -i ) for( int j = y; j <= m; j += j & -j ) bit[i][j] += d; } int query( int x, int y ) { int rt = 0; for( int i = x; i; i -= i & -i ) for( int j = y; j; j -= j & -j ) rt += bit[i][j]; return rt; } int query( int x1, int y1, int x2, int y2 ) { return query(x2,y2) - query(x1-1,y2) - query(x2,y1-1) + query(x1-1,y1-1); } void solve() { while(q--) { char ss[100]; scanf( "%s", ss ); if( ss[0] == ‘m‘ ) { int x, y, d; scanf( "%d%d%d", &x, &y, &d ); modify( x, y, d ); } else { int x1, y1, x2, y2; scanf( "%d%d%d%d", &x1, &y1, &x2, &y2 ); printf( "%d\\n", query(x1,y1,x2,y2) ); } } } }case2;//二维 int main() { freopen ( "matsum.in", "r", stdin ) ; freopen ( "matsum.out", "w", stdout ) ; scanf( "%d%d%d", &n, &m, &q ); if( n == 1 ) case1.solve(); else case2.solve(); }
第二题:裸的线段树模板,注意把值传上去和放下去的地方,写挂了很多次。。。
#include<bits/stdc++.h> using namespace std; const int oo = 0x3f3f3f3f; #define MAX_N 100005 long long sum ; int mn, mx, a[MAX_N]; int n, m; struct SegTree{ struct node{ long long sum; int vmin,vmax,lazy; }; node Tree[MAX_N << 2]; #define ls l, m, v << 1 #define rs m+1, r, v << 1 | 1 void push_up(int v){ Tree[v].sum = Tree[v << 1].sum + Tree[v << 1 | 1].sum; Tree[v].vmin = min(Tree[v << 1].vmin, Tree[v << 1 | 1].vmin); Tree[v].vmax = max(Tree[v << 1].vmax, Tree[v << 1 | 1].vmax); } void push_down(int l, int r, int v){ int m = (l + r) >> 1; Tree[v << 1].sum += 1LL * Tree[v].lazy * (m - l +1); Tree[v << 1].vmin += Tree[v].lazy; Tree[v << 1].vmax += Tree[v].lazy; Tree[v << 1].lazy += Tree[v].lazy; Tree[v << 1 | 1].sum += 1LL * Tree[v].lazy * (r - m); Tree[v << 1 | 1].vmin += Tree[v].lazy; Tree[v << 1 | 1].vmax += Tree[v].lazy; Tree[v << 1 | 1].lazy += Tree[v].lazy; Tree[v].lazy = 0; } void build(int l = 1, int r = n, int v = 1){ Tree[v].sum = Tree[v].vmin = Tree[v].vmax = Tree[v].lazy = 0; if(l ==r){ Tree[v].sum = 1LL * a[l]; Tree[v].vmin = Tree[v].vmax = a[l]; } else { int m = (l +r) >> 1; build(ls); build(rs); push_up(v); } } void modify(int x,int L,int R,int l = 1, int r = n, int v = 1){ if(l >= L && r <= R){// += x WA了8次,每次都用的lazy... Tree[v].lazy += x; Tree[v].sum += 1LL * x * (r - l + 1); Tree[v].vmin += x; Tree[v].vmax += x; } else { if(Tree[v].lazy)push_down(l,r,v); int m = (l + r) / 2; if(L <= m)modify(x,L,R,ls); if(R > m)modify(x,L,R,rs); push_up(v); } } node query(int L,int R,int l = 1, int r = n,int v = 1){ if(l >= L && r <= R) return Tree[v]; else { if(Tree[v].lazy) push_down(l,r,v); int m = (l + r) / 2; node ans; ans.vmin = oo, ans.vmax = -oo, ans.sum = 0;//init if(L <= m){ node nw = query(L,R,ls); ans.vmin = min(ans.vmin, nw.vmin); ans.vmax = max(ans.vmax, nw.vmax); ans.sum += nw.sum; } if(R > m){ node nw = query(L,R,rs); ans.vmin = min(ans.vmin, nw.vmin); ans.vmax = max(ans.vmax, nw.vmax); ans.sum += nw.sum; } return ans; } } }; SegTree Tr; int main(){ freopen("segsum.in","r",stdin); freopen("segsum.out","w",stdout); cin>>n>>m; for(int i = 1; i <= n; i++) scanf("%d", a + i); Tr.build(); for(int i = 1; i <= m; i++){ string opt; cin>>opt; if(opt[0] == ‘q‘){ int l, r; scanf("%d%d",&l,&r); SegTree::node nw; nw = Tr.query(l,r); cout<<nw.vmin<<" "<<nw.vmax<<" "<<nw.sum<<endl; } else{ int l, r, x; scanf("%d%d%d",&l,&r,&x); Tr. modify(x,l,r); } } }
线段树找了好几个板子,还是郭大侠的最好用,写起来也简单,但还是WA了很多次
注意:modify,query,build时要push_down, query时要push_up
modify是增加的val,不是lazy;
要记住更新m的值,ans给初值
不过指针跑的真的快
#include <bits/stdc++.h> using namespace std; #define maxn 100005 #define ll long long int a[maxn],n,m,s; void read(int &x){ int f=1;x=0;char s=getchar(); while(s<‘0‘||s>‘9‘){if(s==‘-‘)f=-1;s=getchar();} while(s>=‘0‘&&s<=‘9‘){x=x*10+s-‘0‘;s=getchar();} x*=f; } struct Info{ ll sum; int vmin,vmax; Info(){} Info(ll sum,int vimn,int vmax):sum(sum),vmin(vimn),vmax(vmax){} }; Info Merge(const Info &ls,const Info &rs){ return Info((ls.sum + rs.sum), min(ls.vmin, rs.vmin), max(ls.vmax, rs.vmax) ); } struct Node{ Info info; int delta; Node *ls, *rs; void pushdown(int l,int r){ if(!delta)return ;//! int m = (l + r) >> 1; ls->info.sum += 1LL * (m - l +1) * delta; ls->info.vmin += delta; ls->info.vmax += delta; ls->delta += delta; rs->info.sum += 1LL *(r - m) * delta; rs->info.vmin += delta; rs->info.vmax += delta; rs->delta += delta; delta = 0; } void update(){//! info = Merge(ls->info, rs->info); } }pool[maxn * 3],*tail = pool, *root; Node * build(int l = 1,int r = n){ Node *nd = ++tail; if(l == r){ nd->info = Info(a[l],a[l],a[l]); nd->delta = 0; } else { int m = (l + r) >> 1; nd->ls = build(l, m); nd->rs = build(m + 1, r); nd->delta = 0; nd->update(); } return nd; } #define Ls nd->ls, l, m #define Rs nd->rs, m+1, r void modify(int delta, int L, int R, Node *nd, int l = 1, int r = n){ if(l >= L && r <= R){ nd->info.sum += 1LL * delta * (r - l + 1); nd->info.vmin += delta; nd->info.vmax += delta; nd->delta += delta; } else { nd->pushdown(l, r); int m = (l + r) >> 1; if(L <= m)modify(delta, L, R, Ls); if(R > m)modify(delta, L, R, Rs); nd->update(); } } Info query(int L, int R, Node *nd, int l = 1, int r = n ){ if(l >= L && r <= R) return nd->info; else { nd->pushdown(l, r); int m = (l + r) >> 1; if(R <= m)return query(L, R, Ls); else if(L > m)return query(L, R, Rs); else return Merge( query( L, R, Ls ),query( L, R, Rs ) ); } } int main(){ freopen("segsum.in","r",stdin); freopen("segsum.out","w",stdout); read(n), read(m); for(int i = 1; i <= n; i++) read(a[i]); root = build(); for(int i = 1; i <= m; i++){ string opt; cin>>opt; if(opt[0] == ‘q‘){ int l, r; read(l),read(r); Info nw = query(l,r,root); cout<<nw.vmin<<" "<<nw.vmax<<" "<<nw.sum<<endl; } else{ int l, r, x; read(l), read(r), read(x); modify(x,l,r,root); } } }
第三题:利用 gcd 可以取并集的特点,二维st表,将一个矩形分成四部分,求并起来的gcd,
求log是要开到maxn,不是n啊,这个错看了三天,最后还是余力大佬帮忙看出来的
#include<iostream> #include<cstdio> using namespace std; #define RG register//卡常 #define maxn 508 #define P 10 int lo[maxn],d[maxn][maxn][P + 1][P + 1]; int n, m, q; int gcd(int a, int b){ return b == 0 ? a : gcd(b, a%b) ; } void read(int &x){ int f=1;x=0;char s=getchar(); while(s<‘0‘||s>‘9‘){if(s==‘-‘)f=-1;s=getchar(); } while(s<=‘9‘&&s>=‘0‘){ x=x*10+s-‘0‘;s=getchar(); } x*=f; } void init() { for( RG int pi = 0; pi <= P; pi++ ) for( RG int pj = 0; pj <= P; pj++ ) { if( pi == 0 && pj == 0 ) continue; for( RG int i = 1; i + (1<<pi) - 1 <= n; i++ ) for( RG int j = 1; j + (1<<pj) - 1 <= m; j++ ) { if( pi == 0 ) { d[i][j][pi][pj] = gcd( d[i][j][pi][pj-1], d[i][j + (1<<(pj-1))][pi][pj-1] ); } else { d[i][j][pi][pj] = gcd( d[i][j][pi-1][pj], d[i + (1<<(pi-1))][j][pi-1][pj] ); } } } } int query(int x1,int yy,int x2,int y2){ int pi = lo[x2 - x1 + 1], pj = lo[y2 - yy + 1]; int ans1 = gcd(d[x1][yy][pi][pj], d[x2 - (1 << pi) + 1][yy][pi][pj]); int ans2 = gcd(d[x1][y2 - (1 << pj) + 1][pi][pj], d[x2 - (1 << pi) + 1][y2 - (1 << pj) + 1][pi][pj]); return gcd(ans1, ans2); } int main(){ freopen("matgcd.in","r",stdin); freopen("matgcd.out","w",stdout); read(n),read(m),read(q); for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) read(d[i][j][0][0]); lo[0] = -1; for(int i = 1; i < maxn; i++) lo[i] = lo[i / 2] + 1; init(); for(int i = 1; i <= q; i++){ string opt; cin>>opt; int x1,yy,x2,y2; read(x1),read(yy),read(x2),read(y2); printf("%d\\n",query(x1,yy,x2,y2)); } }