前言 难度大致会按排序来 就不写总结啦 嘻嘻 适合刚学线段树的人(和我一样 练手

/*if you can't see the repayWhy not just work step by steprubbish is relaxedto ljq
*/
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <cmath>
#include <map>
#include <stack>
#include <set>
#include <sstream>
#include <vector>
#include <stdlib.h>
#include <algorithm>
using namespace std;#define dbg(x) cout<<#x<<" = "<< (x)<< endl
#define dbg2(x1,x2) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<endl
#define dbg3(x1,x2,x3) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<" "<<#x3<<" = "<<x3<<endl
#define max3(a,b,c) max(a,max(b,c))
#define min3(a,b,c) min(a,min(b,c))
#define lc (rt<<1)
#define rc (rt<<11)
#define mid ((l+r)>>1)typedef pair<int,int> pll;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int _inf = 0xc0c0c0c0;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll _INF = 0xc0c0c0c0c0c0c0c0;
const ll mod =  (int)1e9+7;ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll ksm(ll a,ll b,ll mod){int ans=1;while(b){if(b&1) ans=(ans*a)%mod;a=(a*a)%mod;b>>=1;}return ans;}
ll inv2(ll a,ll mod){return ksm(a,mod-2,mod);}
void exgcd(ll a,ll b,ll &x,ll &y,ll &d){if(!b) {d = a;x = 1;y=0;}else{exgcd(b,a%b,y,x,d);y-=x*(a/b);}}//printf("%lld*a + %lld*b = %lld\n", x, y, d);

这是下面题目的头文件

001.CF1041C 简单的线段树运用

题意 有nnn杯咖啡 每天有mmm个小时工作时间 喝完一杯咖啡必须等ddd时间才能喝下一杯
问你怎么安排使得喝完所有咖啡的天数最小
做法 直接上贪心 set上二分即可 但是本着练练自己线段树的水平写了个线段树
(忘记<<2 这次<<1无限RE 调到思考人生
线段树主要是单点修改 update
区间查询最大值 query
查询大于值等于x的最左边的下标 query_
我们只要对咖啡时间排个序 从最小的咖啡时间开始打上天数cnt 然后找最小的大于他时间+d的即可

const int MAX_N = 200025;
struct node
{ll a;int id;bool operator< (const node other) const{return a < other.a;}
}arr[MAX_N];
int ans[MAX_N],cnt;
ll maxx[MAX_N<<2];
int n,m,d;
void up(int rt)
{maxx[rt] = max(maxx[rt<<1],maxx[rt<<1|1]);
}
void build(int rt,int l,int r)
{if(l==r){maxx[rt] = arr[l].a;return ;}int mid = (l+r)>>1;build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);up(rt);
}
void update(int rt,int l,int r,int x)
{if(l==r){maxx[rt] = 0;return;}int mid = (l+r)>>1;if(x<=mid) update(rt<<1,l,mid,x);else update(rt<<1|1,mid+1,r,x);up(rt);
}
ll query(int rt,int l,int r,int x,int y)
{if(x<=l&&r<=y)return maxx[rt];int mid = (l+r)>>1;ll maxx1 = 0,maxx2 = 0;if(x<=mid) maxx1 = query(rt<<1,l,mid,x,y);if(mid<y) maxx2 = query(rt<<1|1,mid+1,r,x,y);return max(maxx1,maxx2);
}
int query_(int rt,int l,int r,ll x)
{if(l==r){return l;}int mid = (l+r)>>1;if(maxx[rt<<1]>=x) return query_(rt<<1,l,mid,x);else return query_(rt<<1|1,mid+1,r,x);
}
void solve(int i)
{if(i>n) return;ans[arr[i].id] = cnt;ll tmp_ = arr[i].a;update(1,1,n,i);ll tmp = query(1,1,n,i,n);if(tmp>tmp_ + d){int xb = query_(1,1,n,tmp_+d+1);ans[arr[xb].id] = cnt;solve(xb);}else  return;
}
int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);cnt = 1;scanf("%d%d%d",&n,&m,&d);memset(ans,-1,sizeof(ans));for(int i = 1;i<=n;++i) scanf("%lld",&arr[i].a),arr[i].id = i;sort(arr+1,arr+1+n);build(1,1,n);for(int i = 1;i<=n;++i){if(ans[arr[i].id]!=-1) continue;else{ll tmp = query(1,1,n,i,n);if(tmp>arr[i].a+d){solve(i);}else ans[arr[i].id] = cnt,update(1,1,n,i);cnt++;}}printf("%d\n",cnt-1);for(int i = 1;i<=n;++i)i==n?printf("%d\n",ans[i]):printf("%d ",ans[i]);//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

002.P4588
题意 初值为1 给你QQQ个操作 1x1 x1x 操作代表乘上 x 2x2 x2x代表把这个值除去第iii次操作的值
做法 我们建立一颗以查询为序列的线段树 只用update 和up就能完成这个操作
每次输出S[1]

const int MAX_N = 100025;
ll s[MAX_N<<2];
ll arr[MAX_N];
void up(int rt)
{s[rt] = s[rt<<1]*s[rt<<1|1]%MOD;
}
void build(int rt,int l,int r)
{if(l==r){s[rt] = 1;return ;}int mid = (l+r)>>1;build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);up(rt);
}
void update(int rt,int l,int r,int x,int v)
{if(l==r){s[rt] = v;return ;}int mid = (l+r)>>1;if(x<=mid) update(rt<<1,l,mid,x,v);else update(rt<<1|1,mid+1,r,x,v);up(rt);
}
int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);int t,Q,opt;scanf("%d",&t);while(t--){scanf("%d%lld",&Q,&MOD);build(1,1,Q);for(int i = 1;i<=Q;++i){scanf("%d%lld",&opt,&arr[i]);if(opt==1) arr[i]%=MOD,update(1,1,Q,i,arr[i]),printf("%lld\n",s[1]);else{update(1,1,Q,arr[i],1);printf("%lld\n",s[1]);}}}//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

003.FZU 2105
题意 ANDANDAND 0 就是区间赋000 ∣1| 1∣1 就是区间赋 111 异或1异或 1异或1就是区间取反
把每个arr[i]存成4个值 0 - 3 然后进行实现
因为发现区间赋值->区间取反 可以执行
但是 区间取反->区间赋值 会直接变成 区间赋值
所以在update里面我们把 col_[rt][c] = 0 是否需要异或置 000 然后down的时候按照顺序 先赋值 再取反 这就是down的顺序 好题啊!

const int MAX_N = 1000025;
int col[MAX_N<<2][4],col_[MAX_N<<2][4],s[MAX_N<<2][4],arr[MAX_N];
void up(int rt,int c)
{s[rt][c] = s[rt<<1][c] + s[rt<<1|1][c];
}
void down(int rt,int l,int r,int c)
{if(col[rt][c]!=-1){int mid =(l+r)>>1;col[rt<<1][c] = col[rt][c];col[rt<<1|1][c] = col[rt][c];col_[rt<<1][c] = 0;col_[rt<<1|1][c] = 0;s[rt<<1][c] = (mid-l+1)*col[rt][c];s[rt<<1|1][c] = (r-mid)*col[rt][c];col[rt][c] = -1;}if(col_[rt][c]){int mid = (l+r)>>1;col_[rt<<1][c] ^= col_[rt][c];col_[rt<<1|1][c] ^= col_[rt][c];s[rt<<1][c] = (mid-l+1)-s[rt<<1][c];s[rt<<1|1][c] = (r-mid)-s[rt<<1|1][c];col_[rt][c] = 0;}
}
void update(int rt,int l,int r,int x,int y,int v,int c)
{if(x<=l&&r<=y){col[rt][c] = v;col_[rt][c] = 0;s[rt][c] = (r-l+1)*v;return ;}int mid = (l+r)>>1;down(rt,l,r,c);if(x<=mid) update(rt<<1,l,mid,x,y,v,c);if(mid<y) update(rt<<1|1,mid+1,r,x,y,v,c);up(rt,c);
}
void update_(int rt,int l,int r,int x,int y,int c)
{if(x<=l&&r<=y){col_[rt][c] ^= 1;s[rt][c] = r-l+1-s[rt][c];return ;}int mid = (l+r)>>1;down(rt,l,r,c);if(x<=mid) update_(rt<<1,l,mid,x,y,c);if(mid<y) update_(rt<<1|1,mid+1,r,x,y,c);up(rt,c);
}
void update__(int rt,int l,int r,int x,int c)
{if(l==r){s[rt][c] = 1;return ;}int mid = (l+r)>>1;if(x<=mid) update__(rt<<1,l,mid,x,c);else update__(rt<<1|1,mid+1,r,x,c);up(rt,c);
}
int query(int rt,int l,int r,int x,int y,int c)
{if(x<=l&&r<=y){return s[rt][c];}down(rt,l,r,c);int mid = (l+r)>>1,res= 0;if(x<=mid) res+=query(rt<<1,l,mid,x,y,c);if(mid<y) res+=query(rt<<1|1,mid+1,r,x,y,c);return res;
}int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);int t;scanf("%d",&t);while(t--){memset(s,0,sizeof(s));memset(col,-1,sizeof(col));memset(col_,0,sizeof(col_));char str[10];int n,Q,opt,x,y,v;scanf("%d%d",&n,&Q);for(int i = 1;i<=n;++i){scanf("%d",&arr[i]);for(int j = 0;j<4;++j){if(arr[i]&(1<<j)){update__(1,1,n,i,j);}}}while(Q--){scanf("%s",str);if(str[0]=='A'){scanf("%d%d%d",&v,&x,&y);x++,y++;for(int i = 0;i<4;++i){if(!(v&(1<<i))){update(1,1,n,x,y,0,i);}}}else if(str[0]=='O'){scanf("%d%d%d",&v,&x,&y);x++,y++;for(int i = 0;i<4;++i){if(v&(1<<i)){update(1,1,n,x,y,1,i);}}}else if(str[0]=='X'){scanf("%d%d%d",&v,&x,&y);x++,y++;for(int i = 0;i<4;++i){if(v&(1<<i)){update_(1,1,n,x,y,i);}}}else if(str[0]=='S'){scanf("%d%d",&x,&y);x++,y++;int ans = 0;for(int i = 0;i<4;++i){ans+=(1<<i)*query(1,1,n,x,y,i);}printf("%d\n",ans);}}}//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

004.BZOJ 5312
因为这个是区间max 所以我们没必要按照位来建立线段树 直接硬核线段树即可
这是与或 我们用随机算法成功卡掉了特殊数据 如何卡的呢?就是如果你区间的maxx 等于你区间的minn 那么你就直接修改maxx和minn包括lazy数组即可 复杂度不会算 卡掉了最差情况

const int MAX_N = 200025;
int maxx[MAX_N<<2],col[MAX_N<<2],minn[MAX_N<<2];
void up(int rt)
{maxx[rt] = max(maxx[rt<<1],maxx[rt<<1|1]);minn[rt] = min(minn[rt<<1],minn[rt<<1|1]);
}
void build(int rt,int l,int r)
{col[rt] = -1;if(l==r){int v;scanf("%d",&v);maxx[rt] = minn[rt] = v;return ;}build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);up(rt);
}
void down(int rt,int l,int r)
{if(col[rt]!=-1){col[rt<<1] = col[rt<<1|1] = col[rt];maxx[rt<<1] = maxx[rt<<1|1] = col[rt];minn[rt<<1] = minn[rt<<1|1] = col[rt];col[rt] = -1;}
}
void And(int rt,int l,int r,int x,int y,int v)
{if(x<=l&&r<=y&&maxx[rt]==minn[rt]){maxx[rt] = maxx[rt]&v;minn[rt] = minn[rt]&v;col[rt] = maxx[rt];return ;}down(rt,l,r);if(x<=mid) And(rt<<1,l,mid,x,y,v);if(mid<y) And(rt<<1|1,mid+1,r,x,y,v);up(rt);
}
void Or(int rt,int l,int r,int x,int y,int v)
{if(x<=l&&r<=y&&maxx[rt]==minn[rt]){maxx[rt] = maxx[rt]|v;minn[rt] = minn[rt]|v;col[rt] = maxx[rt];return ;}down(rt,l,r);if(x<=mid) Or(rt<<1,l,mid,x,y,v);if(mid<y) Or(rt<<1|1,mid+1,r,x,y,v);up(rt);
}
int query(int rt,int l,int r,int x,int y)
{if(x<=l&&r<=y){return maxx[rt];}down(rt,l,r);int maxx1 = 0,maxx2 = 0;if(x<=mid) maxx1 = query(rt<<1,l,mid,x,y);if(mid<y) maxx2 = query(rt<<1|1,mid+1,r,x,y);return max(maxx1,maxx2);
}
int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);int n,Q,opt,x,y,v;scanf("%d%d",&n,&Q);build(1,1,n);while(Q--){scanf("%d",&opt);if(opt==1){scanf("%d%d%d",&x,&y,&v);And(1,1,n,x,y,v);}else if(opt==2){scanf("%d%d%d",&x,&y,&v);Or(1,1,n,x,y,v);}else{scanf("%d%d",&x,&y);printf("%d\n",query(1,1,n,x,y));}}//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

我们继续尝试下正解

const int MAX_N = 200025;
int maxx[MAX_N<<2],same[MAX_N<<2];
void up(int rt)
{maxx[rt] = max(maxx[rt<<1],maxx[rt<<1|1]);same[rt] = (~(maxx[rt<<1]^maxx[rt<<1|1]))&(same[rt<<1]&same[rt<<1|1]);
}
void build(int rt,int l,int r)
{if(l==r){int v;scanf("%d",&v);same[rt] = inf;maxx[rt] = v;return ;}build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);up(rt);
}
void pushnow(int rt,int sm,int mx)
{same[rt] |= sm;maxx[rt] = (mx&sm)|(maxx[rt]&(~sm));
}
void down(int rt)
{pushnow(rt<<1,same[rt],maxx[rt]);pushnow(rt<<1|1,same[rt],maxx[rt]);
}
void update(int rt,int l,int r,int x,int y,int sm,int v)
{if(y<l||x>r) return;if(x<=l&&r<=y){if(l==r||(sm&same[rt])==sm){maxx[rt] = (maxx[rt]&(~sm))|(v&sm);return ;}}down(rt);update(rt<<1,l,mid,x,y,sm,v);update(rt<<1|1,mid+1,r,x,y,sm,v);up(rt);
}
int query(int rt,int l,int r,int x,int y)
{if(y<l||x>r) return 0;if(x<=l&&r<=y){return maxx[rt];}down(rt);int maxx1 = 0,maxx2 = 0;maxx1 = query(rt<<1,l,mid,x,y);maxx2 = query(rt<<1|1,mid+1,r,x,y);return max(maxx1,maxx2);
}
int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);int n,Q,opt,x,y,v;scanf("%d%d",&n,&Q);build(1,1,n);while(Q--){scanf("%d",&opt);if(opt==1){scanf("%d%d%d",&x,&y,&v);update(1,1,n,x,y,inf^v,0);}else if(opt==2){scanf("%d%d%d",&x,&y,&v);update(1,1,n,x,y,v,v);}else{scanf("%d%d",&x,&y);printf("%d\n",query(1,1,n,x,y));}}//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

005 Hdu 3911
题意 给你一个包含n nn元素的01序列 有m次操作 0操作 输出 x 到 y 之间最长1个数
1 操作 将x y 区间内所有数取反
这道题去年六月份写过题解 不过是看不懂的一句话题解 今天重新总结一下这个题
类似这种区间可以通过一种性质 (左边,右边,中间)得到的题 我们用线段树去做
这题我们定义两个结构体 arr1 和 arr2 arr1就是原序列中 连续 1 的一颗树
arr2 就是 原序列中 连续 0的一棵树
你既然 1 操作使得一段区间反转 那么直接 1树和0树换一下即可

int Max(int a,int b)
{if(a>b) return a;return b;
}
int Min(int a,int b)
{if(a<b) return a;return b;
}
void Swap(int &x,int &y)
{int t = x;x = y;y = t;
}
const int MAX_N = 100025;
struct node
{int down,ls0,rs0,ls1,rs1,ms0,ms1;
}arr[MAX_N<<2];
void up(int rt,int l,int r)
{arr[rt].ls1 = arr[rt<<1].ls1;arr[rt].rs1 = arr[rt<<1|1].rs1;if(arr[rt].ls1==mid-l+1) arr[rt].ls1+=arr[rt<<1|1].ls1;if(arr[rt].rs1==r-mid) arr[rt].rs1+=arr[rt<<1].rs1;arr[rt].ms1 = Max(arr[rt<<1].ms1,arr[rt<<1|1].ms1);arr[rt].ms1 = Max(arr[rt].ms1,Max(Max(arr[rt].ls1,arr[rt].rs1),arr[rt<<1].rs1+arr[rt<<1|1].ls1));arr[rt].ls0 = arr[rt<<1].ls0;arr[rt].rs0 = arr[rt<<1|1].rs0;if(arr[rt].ls0==mid-l+1) arr[rt].ls0+=arr[rt<<1|1].ls0;if(arr[rt].rs0==r-mid) arr[rt].rs0+=arr[rt<<1].rs0;arr[rt].ms0 = Max(arr[rt<<1].ms0,arr[rt<<1|1].ms0);arr[rt].ms0 = Max(arr[rt].ms0,Max(Max(arr[rt].ls0,arr[rt].rs0),arr[rt<<1].rs0+arr[rt<<1|1].ls0));
}
void SWAP(int rt)
{Swap(arr[rt].ls1,arr[rt].ls0);Swap(arr[rt].rs1,arr[rt].rs0);Swap(arr[rt].ms1,arr[rt].ms0);
}
void down(int rt,int l,int r)
{if(arr[rt].down){arr[rt].down^=1;arr[rt<<1].down^=1;arr[rt<<1|1].down^=1;SWAP(rt<<1),SWAP(rt<<1|1);}
}
void build(int rt,int l,int r)
{arr[rt].down = arr[rt].ls0 = arr[rt].ls1=arr[rt].ms0=arr[rt].ms1=arr[rt].ms0 =arr[rt].rs0=arr[rt].rs1= 0;if(l==r){int v;scanf("%d",&v);if(v){arr[rt].ls1 = arr[rt].rs1 = arr[rt].ms1 = 1;}else{arr[rt].ls0 = arr[rt].rs0 = arr[rt].ms0 = 1;}return ;}build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);up(rt,l,r);
}
void update(int rt,int l,int r,int x,int y)
{if(x<=l&&r<=y){arr[rt].down^=1;SWAP(rt);return ;}down(rt,l,r);if(x<=mid) update(rt<<1,l,mid,x,y);if(mid<y) update(rt<<1|1,mid+1,r,x,y);up(rt,l,r);
}
node query(int rt,int l ,int r,int x,int y)
{if(x<=l&&r<=y){return arr[rt];}down(rt,l,r);if(x<=mid&&y>mid){node tmp1 = query(rt<<1,l,mid,x,y);node tmp2 = query(rt<<1|1,mid+1,r,x,y);node ans;ans.ls1 = tmp1.ls1,ans.ls0 = tmp1.ls0;ans.rs0 = tmp2.rs0,ans.rs1 = tmp2.rs1;if(ans.ls1 == mid-l+1) ans.ls1+=tmp2.ls1;if(ans.ls0==mid-l+1) ans.ls0+=tmp2.ls0;if(ans.rs0==r-mid) ans.rs0+=tmp1.rs0;if(ans.rs1==r-mid) ans.rs1 += tmp1.rs1;ans.ms1 = Max(Max(Max(tmp1.ms1,tmp2.ms1),Max(ans.ls1,ans.rs1)),tmp1.rs1+tmp2.ls1);return ans;}else if(x<=mid){return query(rt<<1,l,mid,x,y);}else if(x>mid){return query(rt<<1|1,mid+1,r,x,y);}
}
int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);int n,Q,opt,x,y;while(scanf("%d",&n)==1){build(1,1,n);scanf("%d",&Q);while(Q--){scanf("%d%d%d",&opt,&x,&y);if(opt==0){printf("%d\n",query(1,1,n,x,y).ms1);}else{update(1,1,n,x,y);}}}//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

006 HDU5634
题意 给你一段区间 有3个操作 1操作 把区间内所有数变成他们的欧拉函数
2操作 把区间内所有数替换成一个值
3操作 求区间和
因为一个数的欧拉函数取很多次以后就不变了 我们只要用一个same数组去作为节省时间的方法来节省时间 复杂度是应该是 O(nlogn)的
然后在change的时候忘记 l==r return 导致一直re
phi[1]没初始化为1 导致一直 tle

const int MAX_N = 10000025;
bool vis[MAX_N];
int n;
ll cnt,prime[MAX_N],phi[MAX_N];
void sieve(int n)
{phi[1] = 1;memset(vis,false,sizeof(vis));cnt = 0;for(int i = 2;i<n;++i){if(!vis[i]){prime[cnt++] = i;phi[i] = i - 1;}for(int j = 0;j<cnt&&i*prime[j]<n;++j){int k = i *prime[j];vis[k] = true;if(i%prime[j]==0){phi[k] = phi[i] * prime[j];break;}phi[k] = phi[i] * (prime[j]-1);}}
}
ll same[300025*4],s[300025*4];
int Max(int a,int b){if(a>b) return a;return b;}
int Min(int a,int b){if(a<b) return a;return b;}
void up(int rt)
{s[rt] = s[rt<<1] + s[rt<<1|1];same[rt] = (same[rt<<1]==same[rt<<1|1])?same[rt<<1]:0;
}
void build(int rt,int l,int r)
{s[rt] = 0,same[rt] = 0;if(l==r){scanf("%lld",&s[rt]);same[rt] = s[rt];return ;}build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);up(rt);
}
void down(int rt,int l,int r)
{if(same[rt]){s[rt<<1] = 1ll*(mid-l+1)*same[rt];s[rt<<1|1] = 1ll*(r-mid)*same[rt];same[rt<<1] = same[rt];same[rt<<1|1] = same[rt];same[rt] = 0;}
}
void update(int rt,int l,int r,int x,int y,ll v)
{if(x<=l&&r<=y){same[rt] = v;s[rt] = 1ll*(r-l+1)*v;return ;}down(rt,l,r);if(x<=mid) update(rt<<1,l,mid,x,y,v);if(mid<y) update(rt<<1|1,mid+1,r,x,y,v);up(rt);
}
void change(int rt,int l,int r,int x,int y)
{if(x>r||y<l) return;if(x<=l&&r<=y&&same[rt]){same[rt] = phi[same[rt]];s[rt] = 1ll*(r-l+1)*same[rt];return ;}if(l==r) return;down(rt,l,r);if(x<=mid) change(rt<<1,l,mid,x,y);if(mid<y) change(rt<<1|1,mid+1,r,x,y);up(rt);
}
ll query(int rt,int l,int r,int x,int y)
{if(x<=l&&r<=y){return s[rt];}down(rt,l,r);ll res = 0;if(x<=mid) res+=query(rt<<1,l,mid,x,y);if(mid<y) res+=query(rt<<1|1,mid+1,r,x,y);up(rt);return res;
}
int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);int t,opt,x,y,z;sieve(MAX_N);scanf("%d",&t);while(t--){int Q;scanf("%d%d",&n,&Q);build(1,1,n);while(Q--){scanf("%d%d%d",&opt,&x,&y);if(opt==1){change(1,1,n,x,y);}else if(opt==2){scanf("%d",&z);update(1,1,n,x,y,1ll*z);}else if(opt==3){printf("%lld\n",query(1,1,n,x,y));}}}//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

007 P2894

其实这道题以前也写过博客
可惜当时年纪轻轻不懂事 竟然在学算法的第一个月学XX线段树不说 还竟然用一句话题解
这题我们怎么做呢?我们要记录 前缀最长连续0的个数 ls
后缀最长连续0的个数 rs
区间最长连续0的个数 ms
然后区间替换和一般的区间替换做法相同
找的时候先找左儿子的 ms是否大于 v
如果不满足 找 左儿子rs+右儿子ls 是否大于v
如果还不满足 就找右儿子 ms是否大于v
然后找到以后还要区间替换成 1 即可
2操作就是裸的区间替换成 0了
代码如下

const int MAX_N = 50025;
int Max(int a,int b)
{if(a>b) return a;return b;
}
int Min(int a,int b)
{if(a<b) return a;return b;
}
struct node
{int ls,rs,ms,down;
}arr[MAX_N<<2];
void up(int rt,int l,int r)
{arr[rt].ls = arr[rt<<1].ls;arr[rt].rs = arr[rt<<1|1].rs;if(arr[rt].ls==mid-l+1) arr[rt].ls += arr[rt<<1|1].ls;if(arr[rt].rs==r-mid) arr[rt].rs += arr[rt<<1].rs;arr[rt].ms = Max(arr[rt<<1].ms,arr[rt<<1|1].ms);arr[rt].ms = Max(arr[rt].ms,Max(arr[rt<<1].rs+arr[rt<<1|1].ls,Max(arr[rt].ls,arr[rt].rs)));
}
void down(int rt,int l,int r)
{if(arr[rt].down!=-1){arr[rt<<1].down = arr[rt].down;arr[rt<<1|1].down = arr[rt].down;arr[rt<<1].rs = arr[rt<<1].ms = arr[rt<<1].ls = (mid- l+1)*(1-arr[rt].down);arr[rt<<1|1].ls = arr[rt<<1|1].rs = arr[rt<<1|1].ms = (r-mid)*(1-arr[rt].down);arr[rt].down = -1;}
}
void build(int rt,int l,int r)
{arr[rt].down = -1;arr[rt].ls = 0,arr[rt].rs = 0,arr[rt].ms = 0;if(l==r){arr[rt].ls = arr[rt].rs = arr[rt].ms = 1;return ;}build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);up(rt,l,r);
}
void update(int rt,int l,int r,int x,int y,int v)
{if(x<=l&&r<=y){arr[rt].down = v;arr[rt].ls = arr[rt].rs = arr[rt].ms = (1-v)*(r-l+1);return ;}down(rt,l,r);if(x<=mid) update(rt<<1,l,mid,x,y,v);if(mid<y) update(rt<<1|1,mid+1,r,x,y,v);up(rt,l,r);
}
int query(int rt,int l,int r,int x,int y,int v)
{if(l==r){return l;}down(rt,l,r);if(arr[rt<<1].ms>=v)    return query(rt<<1,l,mid,x,y,v);else if(arr[rt<<1].rs+arr[rt<<1|1].ls>=v) return mid - arr[rt<<1].rs + 1;else if(arr[rt<<1|1].ms>=v) return query(rt<<1|1,mid+1,r,x,y,v);
}
int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);int n,Q,opt,x,y;scanf("%d%d",&n,&Q);build(1,1,n);while(Q--){scanf("%d%d",&opt,&x);if(opt==1){if(arr[1].ms<x){printf("0\n");}else{int ans = query(1,1,n,1,n,x);printf("%d\n",ans);update(1,1,n,ans,ans+x-1,1);}}else{scanf("%d",&y);update(1,1,n,x,x+y-1,0);}}//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

008 BZOJ 5028
题目给你这一段瓶子能倒出来的最小水 其实不就是
ax + by + cz +… km的最小整数解么
用裴蜀定理可知这个解就是他们的区间gcd
那么答案就转换成区间gcd 和 区间更新
线段树如果要维护这些东西 肯定是要超时的
那么我们怎么做呢?
我们知道 gcd(x,y) = gcd(x,y-x)
gcd(x,y,z) = gcd(x,gcd(y,z))
那么x->y的区间gcd 就可以化成 arr[x] 和 差分线段树上 x+1和y的gcd
前面arr[x] 我们用一个树状数组去维护
后面的x+1和y的gcd的线段树 因为gcd满足区间传递性 所以直接维护即可
可能代码写的有点丑 实际上只要求abs 即可 但是abs 在 long long 容易出问题 所以写了个特判

const int MAX_N = 200005;
ll arr[MAX_N],s[MAX_N<<2],C[MAX_N<<1];
void add(int x,int v)
{for(;x<MAX_N;x+=x&(-x))C[x]+=v;
}
int getsum(int x)
{int res= 0;for(;x;x-=x&(-x))res+=C[x];return res;
}
void up(int rt)
{s[rt] = gcd(s[rt<<1],s[rt<<1|1]);
}
void build(int rt,int l,int r)
{if(l==r){if(l==1) s[rt] = arr[1];else s[rt] = arr[l] - arr[l-1];return ;}build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);up(rt);
}
void update(int rt,int l,int r,int x,int v)
{if(l==r){s[rt]+=v;return;}if(x<=mid) update(rt<<1,l,mid,x,v);else update(rt<<1|1,mid+1,r,x,v);up(rt);
}
ll query(int rt,int l,int r,int x,int y)
{if(x<=l&&r<=y){return s[rt];}if(x>mid) return query(rt<<1|1,mid+1,r,x,y);if(y<=mid) return query(rt<<1,l,mid,x,y);return gcd(query(rt<<1,l,mid,x,y),query(rt<<1|1,mid+1,r,x,y));
}
int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);int n,Q,opt,x,y,v;scanf("%d%d",&n,&Q);for(int i = 1;i<=n;++i) scanf("%lld",&arr[i]);build(1,1,n);while(Q--){scanf("%d%d%d",&opt,&x,&y);if(opt==1){if(x==y){ll ans = arr[x]+getsum(x);if(ans<0) ans*=-1;printf("%lld\n",ans);}else{ll ans = gcd(arr[x]+getsum(x),query(1,1,n,x+1,y));if(ans<0) ans*=-1;printf("%lld\n",ans);}}else{scanf("%d",&v);add(x,v);add(y+1,-v);update(1,1,n,x,v);update(1,1,n,y+1,-v);}}//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

009 CF914D
题意 给你n个数 两个操作
2操作 单点修改
1操作 x y v 问你x 和 y区间内的数gcd在修改次数小于等于1的时候能否为v
我们先维护一个区间gcd
然后去很骚的查询即可 查询大抵就是剪枝的思想 我找到如果一个区间%v==0 那么我就不用查下去了
否则到根如果gcd%v!=0 那么我就需要更改一次
这是这个思路写的代码

const int MAX_N = 500025;
ll s[MAX_N<<2];
int tmp;
void up(int rt)
{s[rt] = gcd(s[rt<<1],s[rt<<1|1]);
}
void build(int rt,int l,int r)
{if(l==r){scanf("%lld",&s[rt]);return ;}build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);up(rt);
}
void update(int rt,int l,int r,int x,int v)
{if(l==r){s[rt] = v;return ;}if(x<=mid) update(rt<<1,l,mid,x,v);else update(rt<<1|1,mid+1,r,x,v);up(rt);
}
void ask(int rt,int l,int r,int x,int y,int v)
{if(tmp>=2) return;if(l==r){if(s[rt]%v!=0) tmp++;return;}if(x<=l&&r<=y){if(s[rt]%v!=0){ask(rt<<1,l,mid,x,y,v);ask(rt<<1|1,mid+1,r,x,y,v);}return ;}if(x>mid) return ask(rt<<1|1,mid+1,r,x,y,v);if(y<=mid) return ask(rt<<1,l,mid,x,y,v);ask(rt<<1,l,mid,x,y,v),ask(rt<<1|1,mid+1,r,x,y,v);
}
int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);int n,Q,opt,x,y,v;scanf("%d",&n);build(1,1,n);scanf("%d",&Q);while(Q--){scanf("%d%d%d",&opt,&x,&y);if(opt==1){scanf("%d",&v);tmp = 0;ask(1,1,n,x,y,v);if(tmp>=2) printf("NO\n");else printf("YES\n");}else{update(1,1,n,x,y);}}//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

010 CF877E
题意 给你一棵树
get操作为求这个树子树内开着灯个数
pow操作为使这个子树内开灯状态取反
我们只要用dfs序建立一颗线段树
然后对get操作 区间求和
对于pow操作 区间取反即可

const int MAX_N = 200025;
int p[MAX_N],eid,s[MAX_N<<2],col[MAX_N<<2],low[MAX_N],high[MAX_N],dep,arr[MAX_N],Rank[MAX_N];
struct
{int v,next;
}e[MAX_N<<1];
void init()
{memset(p,-1,sizeof(p));eid = 0;
}
void add(int u,int v)
{e[eid].v = v;e[eid].next = p[u];p[u] = eid++;
}
void dfs(int u,int fa)
{low[u] = ++dep;Rank[dep] = u;for(int i = p[u];~i;i=e[i].next)if(e[i].v!=fa)dfs(e[i].v,u);high[u] = dep;
}
void up(int rt)
{s[rt] = s[rt<<1] + s[rt<<1|1];
}
void build(int rt,int l,int r)
{col[rt] = 0;if(l==r){s[rt] = arr[Rank[l]];return ;}build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);up(rt);
}
void down(int rt,int l,int r)
{if(col[rt]){col[rt<<1] ^= 1;col[rt<<1|1] ^= 1;s[rt<<1] = mid-l+1-s[rt<<1];s[rt<<1|1] = r-mid - s[rt<<1|1];col[rt] = 0;}
}
void update(int rt,int l,int r,int x,int y)
{if(x<=l&&r<=y){col[rt] ^= 1;s[rt] = r-l+1-s[rt];return ;}down(rt,l,r);if(x<=mid) update(rt<<1,l,mid,x,y);if(mid<y) update(rt<<1|1,mid+1,r,x,y);up(rt);
}
int query(int rt,int l,int r,int x,int y)
{if(x<=l&&r<=y){return s[rt];}int res = 0;down(rt,l,r);if(x<=mid) res+=query(rt<<1,l,mid,x,y);if(mid<y) res+=query(rt<<1|1,mid+1,r,x,y);return res;
}
int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);int Q,n,a,x;char str[5];init();scanf("%d",&n);for(int i = 2;i<=n;++i){scanf("%d",&a);add(a,i),add(i,a);}for(int i= 1;i<=n;++i) scanf("%d",&arr[i]);dfs(1,0);build(1,1,n);scanf("%d",&Q);while(Q--){scanf("%s",str);if(str[0]=='g'){scanf("%d",&x);printf("%d\n",query(1,1,n,low[x],high[x]));}else{scanf("%d",&x);update(1,1,n,low[x],high[x]);}}//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

011 BZOJ 3211
题意 区间开方 区间求和 单点修改
实际上就是只要当前的maxx<=1 就不用继续开方了
一开始代码写丑了 一直T 应该是均摊负责度不会T
后来调过了

const int MAX_N = 100025;
int maxx[MAX_N<<2];
ll s[MAX_N<<2];
void up(int rt)
{s[rt] = s[rt<<1] + s[rt<<1|1];maxx[rt] = max(maxx[rt<<1],maxx[rt<<1|1]);
}
void build(int rt,int l,int r)
{if(l==r){scanf("%d",&maxx[rt]);s[rt] = maxx[rt];return ;}build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);up(rt);
}ll query(int rt,int l,int r,int x,int y)
{if(x<=l&&r<=y){return s[rt];}ll res = 0;if(x<=mid) res+= query(rt<<1,l,mid,x,y);if(mid<y) res+=query(rt<<1|1,mid+1,r,x,y);return res;
}
void update(int rt,int l,int r,int x,int y)
{if(maxx[rt]<=1) return;if(l==r){s[rt] = sqrt(s[rt]);maxx[rt] = s[rt];return;}if(y<=mid) update(rt<<1,l,mid,x,y);else if(x>mid) update(rt<<1|1,mid+1,r,x,y);else{update(rt<<1,l,mid,x,y);update(rt<<1|1,mid+1,r,x,y);}up(rt);
}
int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);int n,Q,opt,x,y;scanf("%d",&n);build(1,1,n);scanf("%d",&Q);while(Q--){scanf("%d%d%d",&opt,&x,&y);if(opt==1){printf("%lld\n",query(1,1,n,x,y));}else{update(1,1,n,x,y);}}//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

012 HDOJ5828
题意 给你n个数 有三个操作 1 l r v 给 l - r的数加上v
2 l r 给 l - r 的数都开方
3 l r 求 l - r的数和
从最简单的情况开始 如果没有1操作 只有区间开方 和区间求和 可以参考这题博客
BZOJ 3211
问题是有相加
我们要知道开方有个性质 次数开多了以后 这个区间的maxx - minn的值是会越来越小的
就是这个公式
x+d&ThinSpace;&ThinSpace;−&ThinSpace;&ThinSpace;y+d&ThinSpace;&ThinSpace;&lt;=&ThinSpace;&ThinSpace;x&ThinSpace;&ThinSpace;−&ThinSpace;&ThinSpace;y\sqrt{x+d}\,\,-\,\,\sqrt{y+d}\,\,&lt;=\,\,\sqrt{x}\,\,-\,\,\sqrt{y} x+d​−y+d​<=x​−y​
那么我们知道 当开方的时候 如果这个区间maxx <= 1 那么我们就不用进行开方操作
如果这个区间 maxx - minn > 1 我们则要进行开方操作
为什么在maxx-minn等于1的时候需要特判呢
因为我们发现 例如 3 4 这两个数 他们原先的maxx 是 4 minn 是 3
开过方以后 maxx 是 2 minn 是 1
你会发现 4 -> 2 , 3 -> 1这不是区间加的过程么 所以这可以用区间加优化
还有一种情况 2 3 maxx 是 3 minn 是 2
开过方以后 maxx = minn = 1
那不是区间替换的过程吗?
就是利用这个性质达到优化时间的操作
所以 2 操作就变成了 要吗不操作
要吗区间加
要吗区间特换
要吗暴力修改
然后一开始s没注意全是long long WA 了两发

const int MAX_N = 100025;
int maxx[MAX_N<<2],minn[MAX_N<<2],col[MAX_N<<2],seto[MAX_N<<2];
ll s[MAX_N<<2];
int Max(int a,int b){if(a>b) return a;return b;}
int Min(int a,int b){if(a<b) return a;return b;}
void up(int rt)
{s[rt] = s[rt<<1] + s[rt<<1|1];maxx[rt] = Max(maxx[rt<<1],maxx[rt<<1|1]);minn[rt] = Min(minn[rt<<1],minn[rt<<1|1]);
}
void down(int rt,int l,int r)
{if(seto[rt]!=-1){s[rt<<1] = 1ll*(mid-l+1)*seto[rt];s[rt<<1|1] = 1ll*(r-mid)*seto[rt];maxx[rt<<1] = maxx[rt<<1|1] = seto[rt];minn[rt<<1] = minn[rt<<1|1] = seto[rt];seto[rt<<1] = seto[rt<<1|1] = seto[rt];col[rt<<1] = col[rt<<1|1] = 0;seto[rt]=-1;}if(col[rt]){s[rt<<1] += 1ll*(mid-l+1)*col[rt];s[rt<<1|1] += 1ll*(r-mid)*col[rt];col[rt<<1] += col[rt];col[rt<<1|1] += col[rt];maxx[rt<<1] += col[rt];maxx[rt<<1|1] += col[rt];minn[rt<<1] += col[rt];minn[rt<<1|1] += col[rt];col[rt] = 0;}
}
void build(int rt,int l,int r)
{seto[rt] = -1;col[rt] = 0;maxx[rt] = minn[rt] = s[rt] = 0;if(l==r){scanf("%lld",&s[rt]);maxx[rt] = minn[rt] = s[rt];return ;}build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);up(rt);
}
void change(int rt,int l,int r,int x,int y,int v)
{if(x<=l&&r<=y){s[rt] += 1ll*v*(r-l+1);col[rt] += v;maxx[rt] += v;minn[rt] += v;return ;}down(rt,l,r);if(x<=mid) change(rt<<1,l,mid,x,y,v);if(mid<y) change(rt<<1|1,mid+1,r,x,y,v);up(rt);
}
void update(int rt,int l,int r,int x,int y)
{if(x<=l&&r<=y){if(maxx[rt]<=1) return;if(l==r){s[rt] = floor(sqrt(s[rt]));maxx[rt] = minn[rt] = s[rt];return ;}if(maxx[rt] == minn[rt]){int tmp = maxx[rt];maxx[rt] = minn[rt] = floor(sqrt(tmp));seto[rt] = maxx[rt];col[rt] = 0;s[rt] = 1ll*(r-l+1) * maxx[rt];return;}else if(maxx[rt]-minn[rt]==1){int ta = maxx[rt],tb = minn[rt];maxx[rt] = floor(sqrt(maxx[rt]));minn[rt] = floor(sqrt(minn[rt]));if(ta-maxx[rt]==tb-minn[rt]){col[rt] += maxx[rt] - ta;s[rt] += 1ll*(maxx[rt]-ta)*(r-l+1);}else{col[rt] = 0;seto[rt] = maxx[rt];s[rt] = 1ll*(r-l+1)*maxx[rt];}return;}down(rt,l,r);update(rt<<1,l,mid,x,y);update(rt<<1|1,mid+1,r,x,y);up(rt);return ;}down(rt,l,r);if(x<=mid) update(rt<<1,l,mid,x,y);if(mid<y) update(rt<<1|1,mid+1,r,x,y);up(rt);
}
ll query(int rt,int l,int r,int x,int y)
{if(x<=l&&r<=y){return s[rt];}ll res =0;down(rt,l,r);if(x<=mid) res+= query(rt<<1,l,mid,x,y);if(mid<y) res+=query(rt<<1|1,mid+1,r,x,y);return res;
}
int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);int t,n,Q,opt,x,y,v;scanf("%d",&t);while(t--){scanf("%d%d",&n,&Q);build(1,1,n);while(Q--){scanf("%d%d%d",&opt,&x,&y);if(opt==1){scanf("%d",&v);change(1,1,n,x,y,v);}else if(opt==2){update(1,1,n,x,y);}else{printf("%lld\n",query(1,1,n,x,y));}}}//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

013 cf 160E
题意 给你 n辆巴士的起始坐标 终止坐标 和启动时间
Q个人的起始坐标 终止坐标 和启动时间
只要满足人的起始坐标在巴士的起始坐标之后 并且终止坐标在巴士的终止坐标之前
并且时间在巴士的启动时间之前 人就可以坐上这巴士
问你每个人能坐上启动时间最早的巴士是哪一辆 坐不上就输出-1
做法 我们首先将所有人和巴士的坐标合在一起按照第一关键字起始坐标排序
第二关键字 id (这样能保证相同时间车一定在人前面
这样你问每个人的时候能保证之前处理过的巴士的起始时间都在这个人前面
然后我们怎么处理呢?
因为要时间最小 所以以时间为节点建立线段树 每个节点存的是该时间最大的 终止坐标
那么每个人都只要问他时间之后的有没有终止坐标大于他的最小的时间
用线段树查询就可以知道了
然后更新的时候就是往那个时间 插入终止坐标 和 巴士编号

const int MAX_N = 200025;
int b[MAX_N<<1],ans[MAX_N],tm[MAX_N],maxx[MAX_N<<2],id[MAX_N<<2];
int Max(int a,int b){if(a>b) return a;return b;}
struct node
{int l,r,tm,id;bool operator<(const node other) const{if(l==other.l) return id < other.id;return l < other.l;}
}arr[MAX_N<<1];
void up(int rt)
{maxx[rt] = Max(maxx[rt<<1],maxx[rt<<1|1]);
}
void build(int rt,int l,int r)
{maxx[rt] = 0;if(l==r){maxx[rt] = -inf;return ;}build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);up(rt);
}
void update(int rt,int l,int r,int x,int v,int ID)
{if(l==r){maxx[rt] = v;id[rt] = ID;return ;}if(x<=mid) update(rt<<1,l,mid,x,v,ID);else update(rt<<1|1,mid+1,r,x,v,ID);up(rt);
}
int query(int rt,int l,int r,int x,int y,int v)
{if(maxx[rt]<v){return -1;}if(l==r){return id[rt];}int ans = -1;if(maxx[rt<<1]>=v&&x<=mid){ans = query(rt<<1,l,mid,x,y,v);if(ans!=-1) return ans;}return query(rt<<1|1,mid+1,r,x,y,v);
}
int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);int n,Q,cnt = 0,cnt_ = 0;scanf("%d%d",&n,&Q);for(int i = 1;i<=n;++i){scanf("%d%d%d",&arr[i].l,&arr[i].r,&arr[i].tm);arr[i].id = i;tm[++cnt_] = arr[i].tm;b[++cnt] = arr[i].l,b[++cnt] = arr[i].r;}for(int i = n+1;i<=n+Q;++i){scanf("%d%d%d",&arr[i].l,&arr[i].r,&arr[i].tm);arr[i].id = i;tm[++cnt_] = arr[i].tm;b[++cnt] = arr[i].l,b[++cnt] = arr[i].r;}sort(b+1,b+1+cnt);sort(tm+1,tm+1+cnt_);int sz = unique(b+1,b+1+cnt)-b-1;int sz_ = unique(tm+1,tm+1+cnt_) - tm - 1;build(1,1,n+Q);for(int i = 1;i<=n+Q;i++){arr[i].tm = lower_bound(tm+1,tm+1+sz_,arr[i].tm) - tm;arr[i].l = lower_bound(b+1,b+1+sz,arr[i].l) - b;arr[i].r = lower_bound(b+1,b+1+sz,arr[i].r) - b;}sort(arr+1,arr+1+n+Q);for(int i = 1;i<=n+Q;i++){if(arr[i].id <=n){update(1,1,n+Q,arr[i].tm,arr[i].r,arr[i].id);}else{//dbg3(i,arr[i].id,query(1,1,n+Q,arr[i].tm,n+Q,arr[i].r));ans[arr[i].id] = query(1,1,n+Q,arr[i].tm,n+Q,arr[i].r);}}for(int i = n+1;i<=n+Q;++i)printf("%d ",ans[i]);//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

014 CF 19D
题意是嫖过来的
给你一个笛卡尔坐标系,现在要支持三种操作,
第一种操作是添加一个点(x,y),
第二种操作是删除一个点(x,y),
第三种操作是查询严格在点(x,y)右上角的点中,横坐标最小的点,如果有多个点,选择纵坐标最小的那个。
这个题加点 删点 想到了set
然后怎么找严格右上角呢?想到了线段树去找右边区间的maxx大于y+1的那个最左坐标
我们用横坐标当线段树节点 节点存该横坐标下纵坐标的最大值 借助了set 的rbegin实现
代码跑的1500ms 应该有再优化的地方 不过能A就不错了 数组开小RE两发

const int MAX_N = 200025;
int x[MAX_N],y[MAX_N],b[MAX_N<<1],maxx[MAX_N<<3],ans[MAX_N][2];
char str[MAX_N][10];
set<int > st[MAX_N<<1];
map<int ,int > mp;
int Max(int a,int b){if(a>b) return a;return b;}
void up(int rt)
{maxx[rt] = Max(maxx[rt<<1],maxx[rt<<1|1]);
}
void build(int rt,int l,int r)
{maxx[rt] = -1;if(l==r){return ;}build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);up(rt);
}
void update(int rt,int l,int r,int x,int v)
{if(l==r){if(v==1){maxx[rt] = *st[x].rbegin();}else{if(st[x].empty()) maxx[rt] = -1;else maxx[rt] = *st[x].rbegin();}return ;}if(x<=mid) update(rt<<1,l,mid,x,v);else update(rt<<1|1,mid+1,r,x,v);up(rt);
}
int query(int rt,int l,int r,int x,int y,int v)
{if(maxx[rt]<v) return -1;if(l==r){return l;}int ans = -1;if(x<=mid){ans = query(rt<<1,l,mid,x,y,v);if(ans!=-1) return ans;}return query(rt<<1|1,mid+1,r,x,y,v);
}
int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);int n,cnt = 0,N = 400000,CNT=1;scanf("%d",&n);mp[-1] = -1;for(int i = 1;i<=n;++i){scanf("%s",str[i]);scanf("%d%d",&x[i],&y[i]);b[++cnt] = x[i];b[++cnt] = y[i];}sort(b+1,b+1+cnt);int sz = unique(b+1,b+1+cnt)-b-1;for(int i = 1;i<=n;++i){int tmp;tmp = lower_bound(b+1,b+1+sz,x[i])-b;mp[tmp] = x[i];x[i] = tmp;tmp = lower_bound(b+1,b+1+sz,y[i])-b;mp[tmp] = y[i];y[i] = tmp;}build(1,1,N);for(int i = 1;i<=n;++i){if(str[i][0]=='a'){st[x[i]].insert(y[i]);update(1,1,N,x[i],1);}else if(str[i][0]=='f'){int tmp = query(1,1,N,x[i]+1,N,y[i]+1);ans[CNT][0] = mp[tmp];if(ans[CNT][0]!=-1){set<int>::iterator xb = st[tmp].upper_bound(y[i]);ans[CNT][1] = mp[*xb];}CNT++;}else if(str[i][0]=='r'){st[x[i]].erase(y[i]);update(1,1,N,x[i],-1);}}for(int i = 1;i<CNT;++i){printf("%d",ans[i][0]);if(ans[i][0]==-1) printf("\n");else{printf(" %d\n",ans[i][1]);}}//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

015 BZOJ 5334
题意 给你n个操作 1 y 操作是把原值乘上一个 y
2 y 操作是把原值除以第y次操作的值
输出每次操作后的值
因为是乘积
所以你按照修改次数建一颗线段树
那么你 1 操作就是把第 i 次操作单点修改为 y
2 操作就是把第 y 次操作单点修改为1
然后每次输出s[1]即可

const int MAX_N = 100025;
int mod;
ll s[MAX_N<<2];
void up(int rt)
{s[rt] = s[rt<<1] * s[rt<<1|1] %mod;
}
void build(int rt,int l,int r)
{s[rt] = 1;if(l==r){return ;}build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);up(rt);
}
void update(int rt,int l,int r,int x,int v)
{if(l==r){s[rt] = v%mod;return ;}if(x<=mid) update(rt<<1,l,mid,x,v);else update(rt<<1|1,mid+1,r,x,v);up(rt);
}int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);int t,n,x,y;scanf("%d",&t);while(t--){scanf("%d%d",&n,&mod);build(1,1,n);for(int i = 1;i<=n;++i){scanf("%d%d",&x,&y);if(x==1){update(1,1,n,i,y);printf("%lld\n",s[1]);}else{update(1,1,n,y,1);printf("%lld\n",s[1]);}}}//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

016 P1438
题意 给一个区间加一个 首相为k 公差为d 的等差序列
求单点值
先建一颗差分线段树 如果我们给某一个区间 l 到 r 加上一个首相为k 公差为d 的等差序列
是不是相当于 给 l 这个点的差分数组 加上 k
l+1 到 r 的这段数组 加上d
r+1 这个点差分数组加上 -(k+(r-l+1-1)*d)
然后再利用差分数组的前缀和恰好等于这个点的值 我们就可以直接query 加上原数组的值做出来了
这种方法好虽然好 但是个人觉得维护单点值还行 区间和有一点点悬(可能是我理解不够
当然用差分数组性质可以 单次O(r+l)的长度求出来

const int MAX_N = 100025;
int s[MAX_N<<2],arr[MAX_N],col[MAX_N<<2];
void up(int rt)
{s[rt] = s[rt<<1] + s[rt<<1|1];
}
void down(int rt,int l,int r)
{if(col[rt]){col[rt<<1] += col[rt];col[rt<<1|1] += col[rt];s[rt<<1] += (mid-l+1)*col[rt];s[rt<<1|1] += (r-mid)*col[rt];col[rt] = 0;}
}
void update(int rt,int l,int r,int x,int y,int v)
{if(x<=l&&r<=y){col[rt] += v;s[rt] += (r-l+1)*v;return ;}down(rt,l,r);if(x<=mid) update(rt<<1,l,mid,x,y,v);if(mid<y) update(rt<<1|1,mid+1,r,x,y,v);up(rt);
}
int query(int rt,int l,int r,int x,int y)
{if(x<=l&&r<=y){return s[rt];}int res = 0;down(rt,l,r);if(x<=mid) res+=query(rt<<1,l,mid,x,y);if(mid<y) res+=query(rt<<1|1,mid+1,r,x,y);return res;
}
int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);int n,Q,opt,x,y,k,d;scanf("%d%d",&n,&Q);for(int i = 1;i<=n;++i) scanf("%d",&arr[i]);while(Q--){scanf("%d",&opt);if(opt==1){scanf("%d%d%d%d",&x,&y,&k,&d);update(1,1,n,x,x,k);if(y>x)update(1,1,n,x+1,y,d);update(1,1,n,y+1,y+1,-((y-x)*d+k));}else{scanf("%d",&x);printf("%d\n",arr[x]+query(1,1,n,1,x));}}//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

我们试试硬核线段树的版本
之所以硬核线段树 是因为等差序列在区间实际上是满足传递性的(当然等比数列就不满足了
因为这题求单点值 所以我们可以不用up 直接down向下传递就行了
解释在代码里面

const int MAX_N = 100025;
int sta[MAX_N<<2],d[MAX_N<<2],arr[MAX_N];
void down(int rt,int l,int r)
{if(sta[rt]||d[rt]){d[rt<<1] += d[rt];d[rt<<1|1] += d[rt];//公差当然是可以传递的sta[rt<<1] += sta[rt];//左边开始下标要加父亲sta[rt<<1|1] += (mid-l+1)*d[rt]+sta[rt];//右边开始下标要加父亲和左儿子的公差sta[rt] = d[rt] = 0;}
}
void update(int rt,int l,int r,int x,int y,int k,int D)
{if(x<=l&&r<=y){sta[rt]+=k+(l-x)*D;//开始坐标要加开始的k和相差了多少个Dd[rt]+=D;return ;}down(rt,l,r);if(x<=mid) update(rt<<1,l,mid,x,y,k,D);if(mid<y) update(rt<<1|1,mid+1,r,x,y,k,D);
}
int query(int rt,int l,int r,int x)
{if(l==r){return arr[l] + sta[rt];}down(rt,l,r);if(x<=mid) return query(rt<<1,l,mid,x);else return query(rt<<1|1,mid+1,r,x);
}
int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);int n,Q,opt,x,y,K,D;scanf("%d%d",&n,&Q);for(int i = 1;i<=n;++i) scanf("%d",&arr[i]);while(Q--){scanf("%d",&opt);if(opt==1){scanf("%d%d%d%d",&x,&y,&K,&D);update(1,1,n,x,y,K,D);}else{scanf("%d",&x);printf("%d\n",query(1,1,n,x));}}//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

017 对不起这题没法交

输入
13 5 8
8 4 10 4
4 3 4 4
10 2 12 2
8 2 8 4
2 4 6 4
10 3 10 4
12 3 12 4
2 2 4 2

输出
3

备注
【数据范围】
对于 30% 的数据,p≤1000。
对于 70% 的数据,p≤30000。
对于 100% 的数据,p≤400000;m,n≤1000000。

做法 我们首先以横坐标方向建在每个横坐标建立一颗存最长连续0长度的线段树
矩形的左边我们添加
矩形的右边我们删除
然后我们从左往右用双指针扫 能组成的正方形边长最小值 就是 min(r-l+1,ms[1])
如果 ms[1]<r-l+1 那么我们就需要向右边移动 l 所以跑一边双指针即可


const int MAX_N = 1000025;
int ms[MAX_N<<2],ls[MAX_N<<2],rs[MAX_N<<2],col[MAX_N<<2];
int Max(int a,int b){if(a>b) return a;return b;}
vector<int > add[MAX_N/2],del[MAX_N/2];
int x[2][MAX_N/2],y[2][MAX_N/2];
void up(int rt,int l,int r)
{if(col[rt]) {ms[rt] = ls[rt] = rs[rt] = 0;return ;}if(l==r) {ms[rt] = ls[rt] = rs[rt] = 1;return ;}ls[rt] = ls[rt<<1] + (ls[rt<<1]==(mid-l+1)?ls[rt<<1|1]:0);rs[rt] = rs[rt<<1|1] + (rs[rt<<1|1] == (r-mid)?rs[rt<<1]:0);ms[rt] = Max(rs[rt<<1]+ls[rt<<1|1],Max(ms[rt<<1],ms[rt<<1|1]));
}
void build(int rt,int l,int r)
{ms[rt] = ls[rt] = rs[rt] = r-l+1,col[rt] = 0;if(l==r){return ;}build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);
}
void update(int rt,int l,int r,int x,int y,int v)
{if(x<=l&&r<=y){col[rt]+=v;up(rt,l,r);return ;}if(x<=rt) update(rt<<1,l,mid,x,y,v);if(mid<y) update(rt<<1|1,mid+1,r,x,y,v);up(rt,l,r);
}
int query(int rt,int l,int r,int x,int y)
{if(x<=l&&r<=y){return ms[rt];}int maxx1 = 0,maxx2 = 0;if(x<=mid) maxx1 = query(rt<<1,l,mid,x,y);if(mid<y) maxx2 = query(rt<<1|1,mid+1,r,x,y);return Max(maxx1,maxx2);
}
int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);int n,m,p;scanf("%d%d%d",&n,&m,&p);for(int i = 1;i<=p;++i){scanf("%d%d%d%d",&x[0][i],&y[0][i],&x[1][i],&y[1][i]);add[x[0][i]].push_back(i);del[x[1][i]].push_back(i);}build(1,1,m);int l = 1,r = 1,ans = 0;while(r<=n){for(int it = 0;it<add[r].size();it++)update(1,1,m,y[0][add[r][it]],y[1][add[r][it]],1);ans = max(ans,min(ms[1],r-l+1));while(ms[1]<r-l+1){for(int it = 0;it<del[l].size();++it)update(1,1,m,y[0][del[l][it]],y[1][del[l][it]],-1);l++;}r++;}printf("%d\n",ans);//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

018 CF438D
题意 1 操作 区间求和
2 操作区间取模
3 操作 单点修改
这道题自己很玄学
用单点update 只要maxx[rt]<v (取模的数)return 就能过
根据定理每个数只要取模 大小至少减少一半 那么最多log2次
所以时间复杂度是 O(nloglogn)
但是自己写了一个区间 maxx[rt] == minn[rt] 进行区间覆盖的 就一直写错 不知道为啥
还是老老实实发一个单点update的吧

const int MAX_N = 100025;
ll s[MAX_N<<2];
int maxx[MAX_N<<2],minn[MAX_N<<2];
int Max(int a,int b){if(a>b) return a;return b;}
int Min(int a,int b){if(a<b) return a;return b;}
void up(int rt)
{s[rt] = s[rt<<1] + s[rt<<1|1];maxx[rt] = Max(maxx[rt<<1],maxx[rt<<1|1]);minn[rt] = Min(minn[rt<<1],minn[rt<<1|1]);
}
void build(int rt,int l,int r)
{maxx[rt] = minn[rt] = s[rt] = 0;if(l==r){scanf("%lld",&s[rt]);maxx[rt] = minn[rt] = s[rt];return ;}build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);up(rt);
}
void change(int rt,int l,int r,int x,int v)
{if(l==r){maxx[rt] = minn[rt] = s[rt] = v;return ;}if(x<=mid) change(rt<<1,l,mid,x,v);else change(rt<<1|1,mid+1,r,x,v);up(rt);
}
void update(int rt,int l,int r,int x,int y,int v)
{if(maxx[rt]<v) return;if(l==r){int tmp = maxx[rt]%v;s[rt] = maxx[rt] = minn[rt] = tmp;return ;}if(x<=mid) update(rt<<1,l,mid,x,y,v);if(mid<y) update(rt<<1|1,mid+1,r,x,y,v);up(rt);
}
ll query(int rt,int l,int r,int x,int y)
{if(x<=l&&r<=y){return s[rt];}ll res = 0;if(x<=mid) res+=query(rt<<1,l,mid,x,y);if(mid<y) res+=query(rt<<1|1,mid+1,r,x,y);return res;
}
int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);int n,Q,opt,x,y,v;scanf("%d%d",&n,&Q);build(1,1,n);while(Q--){scanf("%d%d%d",&opt,&x,&y);if(opt==1)  printf("%lld\n",query(1,1,n,x,y));else if(opt==3) change(1,1,n,x,y);else{int v;scanf("%d",&v);update(1,1,n,x,y,v);}}//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

019 HDU 6183
其实和CF 19D一样 只不过有50个颜色 你需要分别建立五十棵线段树 手法给别人学的 看看代码吧
做法就是给每个y坐标存该颜色最小值x
动态开点 值得好好回顾

const int MAX_N = 1000025;
int root[52];
int minn[MAX_N<<2],son[MAX_N<<2][2],cnt = 0;
int Min(int a,int b){if(a<b) return a;return b;}
inline void init()
{memset(root,0,sizeof(root));cnt = 0;son[0][0] = son[0][1] = 0;minn[0] = inf;
}
inline void up(int p)
{minn[p] = min(minn[son[p][0]],minn[son[p][1]]);
}
inline void update(int &p,int l,int r,int x,int y)
{if(!p){p=++cnt;minn[p] = y;son[p][0] = son[p][1] = 0;}if(l==r){minn[p] = min(minn[p],y);return ;}int mid = (l+r)>>1;if(x<=mid) update(son[p][0],l,mid,x,y);else update(son[p][1],mid+1,r,x,y);up(p);
}
inline int query(int p,int l,int r,int ql,int qr){if(!p)return inf;if(ql<=l&&r<=qr)return minn[p];int mid=l+r>>1;if(qr<=mid)return query(son[p][0],l,mid,ql,qr);if(ql>mid)return query(son[p][1],mid+1,r,ql,qr);return min(query(son[p][0],l,mid,ql,mid),query(son[p][1],mid+1,r,mid+1,qr));
}
int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);int t,x,y,c;init();while(~scanf("%d",&t)){if(t==3) break;else if(t==0) init();else if(t==1){scanf("%d%d%d",&x,&y,&c);update(root[c],1,1000000,y,x);}else{scanf("%d%d%d",&x,&y,&c);int ans = 0;for(int i = 0;i<=50;++i)ans+=(query(root[i],1,1000000,y,c)<=x);printf("%d\n",ans);}}//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

020 P1471
区间加 区间求平均数 区间求方差
其实这道题的关键在于化方差式子 你把每一项都列开就能得到最后的方差式子为
(a[l]*a[l]+a[l+1]*a[l+1]+…a[r]*a[r])/(r-l+1) - ((a[l]+a[l+1]+…+a[r])/(r-l+1))^2
然后我们只要维护一个 a[i]的平方 维护一个 a[i]
然后用线段树去操作就可以实现了

const int MAX_N = 100025;
double s[MAX_N<<2],s_mul[MAX_N<<2],col[MAX_N<<2];
void up(int rt)
{s[rt] = s[rt<<1] + s[rt<<1|1];s_mul[rt] = s_mul[rt<<1] + s_mul[rt<<1|1];
}
void down(int rt,int l,int r)
{if(col[rt]){col[rt<<1] += col[rt];col[rt<<1|1] += col[rt];s_mul[rt<<1] += (s[rt<<1])*col[rt]*2+col[rt]*col[rt]*(mid-l+1);s_mul[rt<<1|1] += (s[rt<<1|1])*col[rt]*2+col[rt]*col[rt]*(r-mid);s[rt<<1] += (mid-l+1)*col[rt];s[rt<<1|1] += (r-mid)*col[rt];col[rt] = 0;}
}
void build(int rt,int l,int r)
{s[rt] = s_mul[rt] = col[rt] = 0;if(l==r){scanf("%lf",&s[rt]);s_mul[rt] = s[rt]*s[rt];return ;}build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);up(rt);
}
void update(int rt,int l,int r,int x,int y,double v)
{if(x<=l&&r<=y){s_mul[rt]+=v*(s[rt])*2+v*v*(r-l+1);s[rt]+=(r-l+1)*v;col[rt]+=v;return ;}down(rt,l,r);if(x<=mid) update(rt<<1,l,mid,x,y,v);if(mid<y) update(rt<<1|1,mid+1,r,x,y,v);up(rt);
}
double query(int rt,int l,int r,int x,int y,int v)
{if(x<=l&&r<=y){return (v==0?s[rt]:s_mul[rt]);}down(rt,l,r);if(y<=mid) return query(rt<<1,l,mid,x,y,v);if(mid<x) return query(rt<<1|1,mid+1,r,x,y,v);return query(rt<<1,l,mid,x,y,v)+query(rt<<1|1,mid+1,r,x,y,v);
}
int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);int n,Q,opt,x,y;double v;scanf("%d%d",&n,&Q);build(1,1,n);while(Q--){scanf("%d%d%d",&opt,&x,&y);if(opt==1){scanf("%lf",&v);update(1,1,n,x,y,v);}else if(opt==2){double ans = query(1,1,n,x,y,0)/(y-x+1);printf("%.4f\n",ans);}else{double ans = query(1,1,n,x,y,1)/(y-x+1) - query(1,1,n,x,y,0)/(y-x+1)*query(1,1,n,x,y,0)/(y-x+1);printf("%.4f\n",ans);}}//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

021 Cogs 2964
题意 给你n长度的b 数组 b数组是 [1,n]的全排列 然后a数组初始化为0 Q个操作
add l r
query l r
add操作是把a数组区间加1
query操作是查询l−r的⌊a[i]b[i]⌋query操作是查询l-r的\lfloor \frac{a\left[ i \right]}{b\left[ i \right]} \rfloor query操作是查询l−r的⌊b[i]a[i]​⌋
样例数据:

输入
5 12
1 5 2 4 3
add 1 4
query 1 4
add 2 5
query 2 5
add 3 5
query 1 5
add 2 4
query 1 4
add 2 5
query 2 5
add 2 2
query 1 5

输出
1
1
2
4
4
6
做法
一开始想的是其实你暴力改点复杂度是 n+n/2+n/3+…1 = lnn (调和级数
只不过要维护很多东西
其实你反过来想 你为什么要改点 就是当前a[i]+1是b[i]的倍数了
那么是不是意味着 a[i]%b[i] 为 1 了?
所以我们只要维护b[i] - a[i]%b[i] 意思是差多少能让b[i] 进一个1
我们这样建立一颗线段树 然后让num数组与1进行跳 如果到单点则树状数组更新 然后让num[rt] = b[l]
因为你要重新开始这个过程了
如果num[rt] > 1 那么你直接区间减 1 即可
代码还是比较优美的

int Min(int a,int b){if(a<b) return a;return b;}
const int MAX_N = 100025;
int num[MAX_N<<2],C[MAX_N<<1],col[MAX_N<<2],b[MAX_N];
void add(int x,int v)
{for(;x<MAX_N;x+=x&(-x))C[x]+=v;
}
int getsum(int x)
{int res = 0;for(;x;x-=x&(-x))res+=C[x];return res;
}
void up(int rt)
{num[rt] = Min(num[rt<<1],num[rt<<1|1]);
}
void down(int rt,int l,int r)
{if(col[rt]){col[rt<<1] += col[rt];col[rt<<1|1] += col[rt];num[rt<<1] += col[rt];num[rt<<1|1] += col[rt];col[rt] = 0;}
}
void build(int rt,int l,int r)
{num[rt] = col[rt] = 0;if(l==r){num[rt] = b[l];return ;}build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);return ;
}
void update(int rt,int l,int r,int x,int y)
{if(l==r&&num[rt]==1) {num[rt]=b[l];add(l,1);return;}if(x<=l&&r<=y&&num[rt]>1) {col[rt]+=-1;num[rt]-=1;return;}down(rt,l,r);if(x<=mid) update(rt<<1,l,mid,x,y);if(mid<y) update(rt<<1|1,mid+1,r,x,y);up(rt);
}
int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);int n,Q,x,y;char str[10];scanf("%d%d",&n,&Q);for(int i = 1;i<=n;++i) scanf("%d",&b[i]);build(1,1,n);while(Q--){scanf("%s%d%d",str,&x,&y);if(str[0]=='a'){update(1,1,n,x,y);}else{printf("%d\n",getsum(y)-getsum(x-1));}}//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

022 HDU 5316
题意 给你n个元素的序列
1操作 把第 l 个元素值改成 r
0操作 求 l 到 r 满足一个序列期中每个相邻元素在原序列中坐标奇偶性不同的最大和
一开始我的做法 是求坐标为奇的最大连续一段和 坐标为偶的最大一段连续和 两个相加即可
后来想了下题目可以断开加和 所以我这个做法显然是错误的
我们应该维护四个东西 就是维护起始坐标为偶(奇) 终止坐标为偶(奇)的情况合并即可
注意因为输入的数可能是 -1e9 所以我们初始值要设为 -0x7fffffff

const int inf = 0x7fffffff;
const int MAX_N = 100025;
ll Max(ll a,ll b){if(a>b) return a;return b;}
struct node
{ll lo,ll,ol,oo;
}arr[MAX_N<<2];
void up(int rt)
{arr[rt].ll = Max(Max(arr[rt<<1].lo+arr[rt<<1|1].ll,arr[rt<<1].ll+arr[rt<<1|1].ol),Max(arr[rt<<1].ll,arr[rt<<1|1].ll));arr[rt].lo = Max(Max(arr[rt<<1].lo+arr[rt<<1|1].lo,arr[rt<<1].ll+arr[rt<<1|1].oo),Max(arr[rt<<1].lo,arr[rt<<1|1].lo));arr[rt].ol = Max(Max(arr[rt<<1].oo+arr[rt<<1|1].ll,arr[rt<<1].ol+arr[rt<<1|1].ol),Max(arr[rt<<1].ol,arr[rt<<1|1].ol));arr[rt].oo = Max(Max(arr[rt<<1].oo+arr[rt<<1|1].lo,arr[rt<<1].ol+arr[rt<<1|1].oo),Max(arr[rt<<1].oo,arr[rt<<1|1].oo));
}
void build(int rt,int l,int r)
{arr[rt].ll = arr[rt].lo = arr[rt].ol = arr[rt].oo = -inf;if(l==r){ll v;scanf("%lld",&v);if(l%2==1) arr[rt].ll =v;else arr[rt].oo = v;return ;}build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);up(rt);
}
void update(int rt,int l,int r,int x,int v)
{if(l==r){if(l%2==1) arr[rt].ll = 1ll*v;else arr[rt].oo = 1ll*v;return ;}if(x<=mid) update(rt<<1,l,mid,x,v);else update(rt<<1|1,mid+1,r,x,v);up(rt);
}
node query(int rt,int l,int r,int x,int y)
{if(x<=l&&r<=y){return arr[rt];}if(y<=mid) return query(rt<<1,l,mid,x,y);if(mid<x) return query(rt<<1|1,mid+1,r,x,y);node tmp1 = query(rt<<1,l,mid,x,y),tmp2 = query(rt<<1|1,mid+1,r,x,y),ans;ans.ll = Max(Max(tmp1.ll+tmp2.ol,tmp1.lo+tmp2.ll),Max(tmp1.ll,tmp2.ll));ans.lo = Max(Max(tmp1.ll+tmp2.oo,tmp1.lo+tmp2.lo),Max(tmp1.lo,tmp2.lo));ans.ol = Max(Max(tmp1.oo+tmp2.ll,tmp1.ol+tmp2.ol),Max(tmp1.ol,tmp2.ol));ans.oo = Max(Max(tmp1.oo+tmp2.lo,tmp1.ol+tmp2.oo),Max(tmp1.oo,tmp2.oo));return ans;
}
int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);int t,n,Q,opt,x,y;ll v;scanf("%d",&t);while(t--){scanf("%d%d",&n,&Q);build(1,1,n);while(Q--){scanf("%d%d%d",&opt,&x,&y);if(opt==1){update(1,1,n,x,1ll*y);}else{node ans = query(1,1,n,x,y);printf("%lld\n",Max(Max(ans.ll,ans.lo),Max(ans.oo,ans.ol)));}}}//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

023 CF 750 E
给你n长度的字符串 然后给你Q次查询
这Q次 输入 l 和 r 问 l 和 r之间最少删除几个字符能使得不出现2016 出现2017

做法
这道题是相当的难啊(对于我来说
我们先从全局去考虑这个问题 不要管l 和 r 全局如何去求这个最小删除代价呢?
这个问题其实是之前接触过的 我们定义二维dp数组 dp[5][5]
代表五个状态 0 状态 2 都没有
1 状态有 2
2 状态 2 后面有 0
3 状态 有2 0 1
4 状态 有2 0 1 7
dp数组的含义是状态之间转换最小要删除多少个字符
只不过不要 6 所以出现6的时候 dp[3][3] 和 dp[4][4] 都要为1
怎么样去更新dp值呢?类似矩阵乘法的思想 只不过乘法变成加法
取min即可 代表是最小需要删除的字符数
那么我们只要输出dp[0][4]就是答案了

const int MAX_N = 200025;
char str[MAX_N];
int num[MAX_N];
struct node
{int f[5][5];void init(int x){for(int i = 0;i<5;++i)for(int j = 0;j<5;++j)f[i][j]=(i==j?0:inf);if(x==-1){for(int i = 0;i<5;++i)f[i][i] = inf;}else if(x==2){f[0][0] = 1;f[0][1] = 0;}else if(x==0){f[1][1] = 1;f[1][2] = 0;}else if(x==1){f[2][2] = 1;f[2][3] = 0;}else if(x==7){f[3][3] = 1;f[3][4] = 0;}else if(x==6){f[3][3] = 1;f[4][4] = 1;}}
}arr[MAX_N<<2];
node Merge(node a,node b)
{node ret;ret.init(-1);for(int i = 0;i<5;++i)for(int j = 0;j<5;++j)for(int k = 0;k<5;++k)ret.f[i][j] = min(ret.f[i][j],a.f[i][k]+b.f[k][j]);return ret;
}
void build(int rt,int l,int r)
{if(l==r){arr[rt].init(num[l]);return ;}build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);arr[rt] = Merge(arr[rt<<1],arr[rt<<1|1]);
}
node query(int rt,int l,int r,int x,int y)
{if(x<=l&&r<=y){return arr[rt];}if(x>mid) return query(rt<<1|1,mid+1,r,x,y);if(y<=mid) return query(rt<<1,l,mid,x,y);node ans = Merge(query(rt<<1,l,mid,x,y),query(rt<<1|1,mid+1,r,x,y));return ans;
}
int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);int x,y,n,Q;scanf("%d%d",&n,&Q);scanf("%s",str+1);for(int i = 1;i<=n;++i) num[i] = str[i]-'0';build(1,1,n);while(Q--){scanf("%d%d",&x,&y);node ans = query(1,1,n,x,y);printf("%d\n",ans.f[0][4]==inf?-1:ans.f[0][4]);}//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

024 BZOJ 3747
题意

做法 其实这个做法在去年省赛的时候就想到了 但是当时弊队只能想到n^2 暴力去查验
今天再次遇到 实际上讲一下
我们维护一个pre数组 代表这个数之前那次出现的下标
我们首先知道 一个电影他能产生贡献的范围就是 pre[i]+1 到 i
这就是首先一个区间加操作了
那么我们到这步电影 是不是要撤销上步电影的贡献呢?
就是 pre[pre[i]]+1 到 pre[i] 都要减去这些贡献
这样的话我们就可以外层一个n 枚举右端点 用线段树去优化 然后就可以取出最大值答案啦

ll Max(ll a,ll b){if(a>b) return a;return b;}
const int MAX_N = 1000025;
int arr[MAX_N],last[MAX_N],pre[MAX_N];
ll val[MAX_N],maxx[MAX_N<<2],col[MAX_N<<2];
void up(int rt)
{maxx[rt] = Max(maxx[rt<<1],maxx[rt<<1|1]);
}
void down(int rt,int l,int r)
{if(col[rt]){col[rt<<1] += col[rt];col[rt<<1|1] += col[rt];maxx[rt<<1] += col[rt];maxx[rt<<1|1] += col[rt];col[rt] = 0;}
}
void update(int rt,int l,int r,int x,int y,ll v)
{if(x<=l&&r<=y){col[rt] += v;maxx[rt] += v;return ;}down(rt,l,r);if(y<=mid) update(rt<<1,l,mid,x,y,v);else if(x>mid) update(rt<<1|1,mid+1,r,x,y,v);else update(rt<<1,l,mid,x,y,v),update(rt<<1|1,mid+1,r,x,y,v);up(rt);
}
int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);int n,m;ll ans = 0;scanf("%d%d",&n,&m);for(int i = 1;i<=n;++i) scanf("%d",&arr[i]);for(int i = 1;i<=m;++i) scanf("%d",&val[i]);for(int i = 1;i<=n;++i) pre[i] = last[arr[i]],last[arr[i]] = i;for(int i = 1;i<=n;++i){update(1,1,n,pre[i]+1,i,val[arr[i]]);if(pre[i]) update(1,1,n,pre[pre[i]]+1,pre[i],-val[arr[i]]);ans = Max(ans,maxx[1]);}printf("%lld\n",ans);//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

025 BZOJ 4293
题意:有一片n亩的土地,上面要种草。每棵草每次长高a[i]厘米
有Q次操作 d v 代表在D时刻的那秒末 减掉所有高度大于v的部分的草
我们怎么做呢?首先要发现一个性质(跪了
就是你先把a[i]排个序 排序好的a[i]无论如何操作 都是单调不减的
那么我们每次只要找到第一个大于v的位置pos pos到n区间覆盖成v即可
然后之前的s[1] 和 区间覆盖后的s[1]之差就是答案
我们这里setv 代表替换标记 col代表懒标记 下传的是时间也就是草长了多少天
区间覆盖和区间加要先覆盖 因为加了再覆盖等同于直接覆盖

const int MAX_N = 500025;
ll maxx[MAX_N<<2],s[MAX_N<<2],setv[MAX_N<<2],col[MAX_N<<2],sum[MAX_N],arr[MAX_N];
ll Max(ll a,ll b){if(a>b) return a;return b;}
void up(int rt)
{s[rt] = s[rt<<1] + s[rt<<1|1];maxx[rt] = Max(maxx[rt<<1],maxx[rt<<1|1]);
}
void build(int rt,int l,int r)
{setv[rt] = -1;col[rt] = s[rt] = maxx[rt] = 0;if(l==r){return ;}build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);up(rt);
}
void down(int rt,int l,int r)
{if(setv[rt]!=-1){setv[rt<<1] = setv[rt<<1|1] = setv[rt];maxx[rt<<1] = maxx[rt<<1|1] = setv[rt];s[rt<<1] = 1ll*(mid-l+1)*setv[rt];s[rt<<1|1] = 1ll*(r-mid)*setv[rt];col[rt<<1] = col[rt<<1|1] = 0;setv[rt] = -1;}if(col[rt]){maxx[rt<<1] += col[rt]*arr[mid];maxx[rt<<1|1] += col[rt]*arr[r];s[rt<<1] += col[rt]*(sum[mid]-sum[l-1]);s[rt<<1|1] += col[rt]*(sum[r]-sum[mid]);col[rt<<1]+=col[rt];col[rt<<1|1] += col[rt];col[rt] = 0;}
}
/*inline void pushnow(int p,ll v){T[p].sum+=v*(sum[T[p].r]-sum[T[p].l-1]),T[p].maxn+=v*a[T[p].r],T[p].lz+=v;}
inline void pushnown(int p,ll v){T[p].bz=v,T[p].lz=0,T[p].sum=(T[p].r-T[p].l+1)*v,T[p].maxn=v;}
inline void pushdown(int p){if(T[p].bz!=-1)pushnown(lc,T[p].bz),pushnown(rc,T[p].bz),T[p].bz=-1;if(T[p].lz)pushnow(lc,T[p].lz),pushnow(rc,T[p].lz),T[p].lz=0;
}*/
void update(int rt,int l,int r,int x,int y,ll v)
{if(x<=l&&r<=y){setv[rt] = v;maxx[rt] = v;s[rt] = 1ll*(r-l+1)*v;col[rt] = 0;return ;}down(rt,l,r);if(y<=mid) update(rt<<1,l,mid,x,y,v);else if(x>mid) update(rt<<1|1,mid+1,r,x,y,v);else update(rt<<1,l,mid,x,y,v),update(rt<<1|1,mid+1,r,x,y,v);up(rt);
}
int query(int rt,int l,int r,ll v)
{if(l==r){return l;}down(rt,l,r);if(maxx[rt<<1]>v) return query(rt<<1,l,mid,v);else return query(rt<<1|1,mid+1,r,v);
}
int main()
{//ios::sync_with_stdio(false);//freopen("a.txt","r",stdin);//freopen("b.txt","w",stdout);int n,Q;scanf("%d%d",&n,&Q);for(int i = 1;i<=n;++i) scanf("%lld",&arr[i]);build(1,1,n);sort(arr+1,arr+1+n);for(int i = 1;i<=n;++i) sum[i] = sum[i-1] + arr[i];ll d1 = 0,d2,v;while(Q--){scanf("%lld%lld",&d2,&v);s[1] += sum[n]*(d2-d1),maxx[1] += (d2-d1)*arr[n],col[1]+=(d2-d1);ll tmp = s[1];if(maxx[1]<=v) {d1 = d2;printf("0\n");continue;}d1 = d2;int pos = query(1,1,n,v);update(1,1,n,pos,n,v);printf("%lld\n",tmp-s[1]);}//fclose(stdin);//fclose(stdout);//cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;return 0;
}

小清的线段树25题日志01 线段树下你和我 欢乐多又多相关推荐

  1. 小清的树链剖分10题日志01 树链剖分种果子 有你好果子吃的

    声明:由于本人能力尚不优 故无法做出解释文章 此文仅为自己日记 感谢你的阅读 作为队里的数据结构选手 2019西安邀请赛的E题 竟然在有机时的情况下 想到了线段树log^2的拆位做法 但是题目路径把自 ...

  2. [蓝桥杯][2018年第九届真题]日志统计(树状数组)

    题目描述 小明维护着一个程序员论坛.现在他收集了一份"点赞"日志,日志共有N行.其中每一行的格式是: ts id 表示在ts时刻编号id的帖子收到一个"赞". ...

  3. python智慧树判断题_知到智慧树_Python程序设计基础_判断题答案

    知到智慧树_Python程序设计基础_判断题答案 更多相关问题 [问答题,简答题] 简述安全门升压试验的方法. [问答题,简答题] 简述热力除氧的基本条件. [问答题,简答题] 简答汽轮机组停机后造成 ...

  4. LSM树——Log-Structured Merge-Tree数据结构、LSM树设计思想、LSM的数据写入操作、LSM的数据查询操作

    LSM树数据结构 简介 传统关系型数据库,一般都选择使用B+树作为索引结构,而在大数据场景下,HBase.Kudu这些存储引擎选择的是LSM树.LSM树,即日志结构合并树(Log-Structured ...

  5. J.哭泣的阿木木(线段树模板题)

    哭泣的阿木木 Description 没啥用的背景故事: 在远古的恕瑞玛,有一个孤独而又忧郁的灵魂,阿木木.他在世间游荡,只为找到一个朋友.他遭受了一种远古的巫术诅咒,注定忍受永世的孤单,因为被他触碰 ...

  6. 线段树模板题3:区间染色问题

    1.3线段树模板题3:区间染色问题 在DotA游戏中,帕吉的肉钩实际上是大多数英雄中最恐怖的东西.挂钩由长度相同的几个连续的金属棍组成. 现在,帕吉(Pudge)希望对挂接进行一些操作. 让我们将钩子 ...

  7. UVA 12086 Potentiometers(线段树裸题)

    题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem ...

  8. 笛卡尔树 (25 分)笛卡尔树是一种特殊的二叉树,其结点包含两个关键字K1和K2。首先笛卡尔树是关于K1的二叉搜索树,即结点左子树的所有K1值都比该结点的K1值小,右子树则大。其次所有结点的K2关键字

    立志用最少的代码做最高效的表达 笛卡尔树是一种特殊的二叉树,其结点包含两个关键字K1和K2.首先笛卡尔树是关于K1的二叉搜索树,即结点左子树的所有K1值都比该结点的K1值小,右子树则大.其次所有结点的 ...

  9. c语言线段树建树程序,C++算法 线段树

    线段树这个算法,看起来非常高端,而且很有用处,所以还是讲一下下吧. 温馨提示:写线段树前请做好写码5分钟,调试一辈子的准备^-^ 啊直接步入正题-- 首先我们考虑一个题目:有一个序列,要做到单点修改单 ...

最新文章

  1. 巧用CSS的RevealTrans滤镜
  2. ubuntu下编译php扩展的时候报缺少pcre解决办法
  3. MySQl笔记8:把good表中商品名为‘诺基亚xxxx‘的商品,改为‘HTCxxxx‘
  4. .net core Entity Framework 与 EF Core
  5. HDU - 1160 FatMouse's Speed(最长不下降子序列)
  6. 第二章:Java基本语法
  7. 利用docker中的nginx镜像部署angular项目
  8. 安装JAVA8要登录_JDK8的安装及环境配置
  9. 迭代器的简单应用实践
  10. .NET Core 2.1 Preview 2带来网络方面的改进
  11. asrc调试_求助winavr编译的固件超出flash大小
  12. Simulink建模:LKA系统功能状态机建模
  13. URP渲染管线实战教程系列 之URP渲染管线实战解密(一)
  14. 程序员夏天穿格子衫,那么冬天穿什么?答案扎心了哈哈哈哈
  15. php phalcon 安装,安装phalcon 开发工具
  16. 插件用法--视频播放video.js
  17. 扁平化风格博客——后续
  18. 微软账户无法登录(应用商店进不去等)
  19. doito-001(余光中)
  20. android studio怎么改软件扫码界面_一文入门Android逆向

热门文章

  1. 西门子1200与3台欧姆龙E5cc温控器通讯程序
  2. Python爬虫进阶之JS逆向国航登录
  3. ps入门第3天_ps抠图选区的几种方法
  4. Django-simpleUi项目开发流程
  5. 图中奇点数量不可能有奇数个的证明
  6. Mac系统及Xcode快捷键---它的能量超乎你想象
  7. 浙大远程计算机应用基础考试,浙大远程计算机应用基础(A)作业5(含答案).doc
  8. STL——queue、priority_queue、deque
  9. 中国电抗器行业深度调研及投资前景预测研究报告
  10. 计算机基础(二)二进制