线段树的风格是跟学的
单点更新:最最基础的线段树,只更新叶子节点,然后把信息用PushUP(int r)这个函数更新上来
1、
1 #include2 #include 3 #include 4 #define lson l,m,rt<<1 5 #define rson m+1,r,rt<<1|1 6 #define maxlen 50010 7 using namespace std; 8 int sum[maxlen<<2]; 9 void pushup(int rt)10 {11 sum[rt]=sum[rt<<1]+sum[rt<<1|1];12 }13 void build(int l,int r,int rt)14 {15 if(l==r)16 {17 scanf("%d",&sum[rt]);18 return ;19 }20 int m=(l+r)>>1;21 build(lson);22 build(rson);23 pushup(rt);24 }25 void update(int p,int add,int l,int r,int rt)26 {27 if(l==r)28 {29 sum[rt]+=add;30 return;31 }32 int m=(l+r)>>1;33 if(p<=m)34 update(p,add,lson);35 else36 update(p,add,rson);37 pushup(rt);38 }39 int query(int L,int R,int l,int r,int rt)40 {41 if(L<=l&&r<=R)//查询区间大于当前区间直接返回根节点值42 return sum[rt];43 int m=(l+r)>>1;44 int ans=0;45 if(L<=m)46 ans+=query(L,R,lson);47 if(R>m)48 ans+=query(L,R,rson);49 return ans;50 }51 int main ()52 {53 int t,n;54 char cmd[10];55 scanf("%d",&t);56 for(int Case=1;Case<=t;++Case)57 {58 printf("Case %d:\n",Case);59 scanf("%d",&n);60 build(1,n,1);61 while(scanf("%s",&cmd))62 {63 if(cmd[0]=='E')64 break;65 int x,y;66 scanf("%d%d",&x,&y);67 if(cmd[0]=='Q')68 printf("%d\n",query(x,y,1,n,1));69 else if(cmd[0]=='S')70 update(x,-y,1,n,1);71 else72 update(x,y,1,n,1);73 }74 }75 }
2、
1 #include2 #include 3 #include 4 #include 5 #define lson l,m,rt<<1 6 #define rson m+1,r,rt<<1|1 7 #define maxlen 200010 8 using namespace std; 9 int sum[maxlen<<2];10 void pushup(int rt)11 {12 sum[rt]=max(sum[rt<<1],sum[rt<<1|1]);13 }14 void build(int l,int r,int rt)15 {16 if(l==r)17 {18 scanf("%d",&sum[rt]);19 return ;20 }21 int m=(l+r)>>1;22 build(lson);23 build(rson);24 pushup(rt);25 }26 void update(int p,int add,int l,int r,int rt)27 {28 if(l==r)29 {30 sum[rt]=add;31 return;32 }33 int m=(l+r)>>1;34 if(p<=m)35 update(p,add,lson);36 else37 update(p,add,rson);38 pushup(rt);39 }40 int query(int L,int R,int l,int r,int rt)41 {42 if(L<=l&&r<=R)43 return sum[rt];44 int m=(l+r)>>1;45 int ans=0;46 if(L<=m)47 ans=max(ans,query(L,R,lson));48 if(R>m)49 ans=max(ans,query(L,R,rson));50 return ans;51 }52 int main ()53 {54 int t,n,m;55 char cmd[2];56 while(scanf("%d%d",&n,&m)!=EOF)57 {58 build(1,n,1);59 while(m--)60 {61 int x,y;62 scanf("%s%d%d",cmd,&x,&y);63 if(cmd[0]=='Q')64 printf("%d\n",query(x,y,1,n,1));65 else66 update(x,y,1,n,1);67 }68 }69 }
3、
题目大意:给你n个数(0~n-1)组成一个序列,每次把第一个数移到最后去,这样可以得到n个序列,问这些序列中最小的逆序数。
分析:首先来说一下逆序数的求法
例如要求x的逆序数只需要访问(x+1,n)段有多少个数,就是x的逆序数。还有就是求最小逆序数的时候有个巧妙的想法,当把x放入数组的后面,此时的逆序数应该为x没放入最后面之前的逆序总数加上(n-x)再减去(x-1);sum = sum+(n-x[i])-(x[i]-1)。
先上一个暴力的,复杂度O(n^2) 300ms+
1 #include2 #include 3 #define maxlen 5010 4 using namespace std; 5 int s[maxlen]; 6 int n,ans,cnt; 7 int main() 8 { 9 while(scanf("%d",&n)!=EOF)10 {11 cnt=0,ans=99999999;12 for(int i=1;i<=n;i++)13 scanf("%d",&s[i]);14 for(int i=1;i<=n;i++)15 for(int j=i+1;j<=n;j++)16 if(s[i]>s[j]) cnt++;17 for(int i=1;i<=n;i++)18 {19 cnt=cnt-s[i]+n-s[i]-1;20 ans=min(cnt,ans);21 }22 printf("%d\n",ans);23 }24 return 0;25 }
线段树 复杂度O(nlogn) 30ms+
单点更新+区间求和
1 #include2 #include 3 #include 4 #include 5 #define maxlen 5010 6 #define lson l,m,rt<<1 7 #define rson m+1,r,rt<<1|1 8 int sum[maxlen<<2]; 9 int s[maxlen];10 void pushup(int rt)11 {12 sum[rt]=sum[rt<<1]+sum[rt<<1|1];13 }14 void build(int l,int r,int rt)15 {16 sum[rt]=0;17 if(l==r)18 return ;19 int m=(l+r)>>1;20 build(lson);21 build(rson);22 }23 void update(int p,int l,int r,int rt)24 {25 if(l==r)26 {27 sum[rt]++;28 return ;29 }30 int m=(l+r)>>1;31 if(p<=m)32 update(p,lson);33 else34 update(p,rson);35 pushup(rt);36 }37 int query(int L,int R,int l,int r,int rt)38 {39 if(L<=l&&r<=R)40 return sum[rt];41 int m=(l+r)>>1;42 int ans=0;43 if(L<=m)44 ans+=query(L,R,lson);45 if (R>m)46 ans+=query(L,R,rson);47 return ans;48 }49 using namespace std;50 int main ()51 {52 int n;53 while(scanf("%d",&n)!=EOF)54 {55 build(0,n-1,1);56 int sum=0;57 for(int i=0; i
4、
题目大意:给你h*w的矩形,现在在上面贴1*L的告示,每次优先选择最左上的位置,输出输在的行号,如果不能贴输出-1
分析:
- 利用线段树可以求区间的最大值;
- 将位置即h用来建树(h<=n,大了没有意义);
- 树中存储的为该位置还拥有的空间; (没贴一次减去L)
- 若左子树的最大值大于他,就查询左子树,否则查询右子树;
线段树 ,单点更新+区间最大值 2500ms+
1 #include2 #include 3 #include 4 #include 5 #define maxlen 200010 6 #define lson l,m,rt<<1 7 #define rson m+1,r,rt<<1|1 8 using namespace std; 9 int sum[maxlen<<2];10 int h,w,n;11 void pushup(int rt)12 {13 sum[rt]=max(sum[rt<<1],sum[rt<<1|1]);14 }15 void build(int l,int r,int rt)16 {17 sum[rt]=w;18 if(l==r)19 return ;20 int m=(l+r)>>1;21 build(lson);22 build(rson);23 }24 int query(int x,int l,int r,int rt)25 {26 if(l==r)27 {28 sum[rt]-=x;29 return l;30 }31 int m=(l+r)>>1;32 int ans=0;33 if(sum[rt<<1]>=x)34 ans=query(x,lson);35 else36 ans=query(x,rson);37 pushup(rt);38 return ans;39 }40 int main ()41 {42 int x;43 while(scanf("%d%d%d",&h,&w,&n)!=EOF)44 {45 if(h>n)46 h=n;47 build(1,h,1);48 while(n--)49 {50 scanf("%d",&x);51 if(sum[1]
成段更新:
第一次写,学到了一个叫做懒惰标记或者叫做延时标记的东西,所谓的延时标记就是更新的时候不是更新到最底层的叶子节点,而是当覆盖整个子区间的时候做一个标记,当有一次update或者query到这个区间的时候把标记传递下去。
题目大意:
定义操作 i j x把i~j区间的奖牌颜色变为x(1/2/3)
给你n个数(1~n)以及Q个操作最后求1~n 的奖牌加权和(权值就是颜色编号)
分析:
线段树 区间替换+求和
利用上面说的延时标记更新结点,区间求和跟上面的一样,这里只有一次询问且询问区间为[1,n],所以更新完后最终答案就是根结点的sum[1]。
1 #include2 #include 3 #include 4 #define maxlen 100010 5 #define lson l,m,rt<<1 6 #define rson m+1,r,rt<<1|1 7 using namespace std; 8 int sum[maxlen<<2]; 9 int col[maxlen<<2];10 void pushup(int rt)11 {12 sum[rt]=sum[rt<<1]+sum[rt<<1|1];13 }14 void pushdown(int rt,int m)15 {16 if(col[rt])17 {18 col[rt<<1]=col[rt<<1|1]=col[rt];19 sum[rt<<1]=(m-(m>>1))*col[rt];20 sum[rt<<1|1]=(m>>1)*col[rt];21 col[rt]=0;22 }23 }24 void build(int l,int r,int rt)25 {26 sum[rt]=1;27 col[rt]=0;28 if(l==r)29 return ;30 int m=(l+r)>>1;31 build(lson);32 build(rson);33 }34 void update(int L,int R,int c,int l,int r,int rt)35 {36 if(L<=l&&r<=R)37 {38 col[rt]=c;39 sum[rt]=c*(r-l+1);40 return ;41 }42 pushdown(rt,r-l+1);43 int m=(l+r)>>1;44 if(L<=m)45 update(L,R,c,lson);46 if(R>m)47 update(L,R,c,rson);48 pushup(rt);49 }50 int main ()51 {52 int t,n,m;53 scanf("%d",&t);54 for(int Case=1;Case<=t;++Case)55 {56 scanf("%d%d",&n,&m);57 build(1,n,1);58 while(m--)59 {60 int a,b,c;61 scanf("%d%d%d",&a,&b,&c);62 update(a,b,c,1,n,1);63 }64 printf("Case %d: The total value of the hook is %d.\n",Case,sum[1]);65 }66 }
Description
You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval.
Input
The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000.
The second line contains N numbers, the initial values of A1, A2, ... , AN. -1000000000 ≤ Ai ≤ 1000000000.Each of the next Q lines represents an operation."C a b c" means adding c to each of Aa, Aa+1, ... , Ab. -10000 ≤ c ≤ 10000."Q a b" means querying the sum of Aa, Aa+1, ... , Ab.
Output
You need to answer all Q commands in order. One answer in a line.
Sample Input
10 51 2 3 4 5 6 7 8 9 10Q 4 4Q 1 10Q 2 4C 3 6 3Q 2 4
Sample Output
455915
题目大意:
1、Q i j sum[i]+sum[i+1]+...+sum[j]
2、C i j c sum[k]+=c i<=k<=j
分析:跟上面的类似,成段的加减+区间求和
1 #include2 #include 3 #include 4 #include 5 #define maxlen 100010 6 #define lson l,m,rt<<1 7 #define rson m+1,r,rt<<1|1 8 using namespace std; 9 long long sum[maxlen<<2];10 long long add[maxlen<<2];11 void pushup(int rt)12 {13 sum[rt]=sum[rt<<1]+sum[rt<<1|1];14 }15 void pushdown(int rt,int m)16 {17 if(add[rt])18 {19 add[rt<<1]+=add[rt];20 add[rt<<1|1]+=add[rt];21 sum[rt<<1]+=add[rt]*(m-(m>>1));22 sum[rt<<1|1]+=add[rt]*(m>>1);23 add[rt]=0;24 }25 }26 void build(int l,int r,int rt)27 {28 add[rt]=0;29 if(l==r)30 {31 scanf("%I64d",&sum[rt]);32 return ;33 }34 int m=(l+r)>>1;35 build(lson);36 build(rson);37 pushup(rt);38 }39 void update(int L,int R,int c,int l,int r,int rt)40 {41 if(L<=l&&r<=R)42 {43 add[rt]+=c;44 sum[rt]+=c*(r-l+1);45 return ;46 }47 pushdown(rt,r-l+1);48 int m=(l+r)>>1;49 if(L<=m)50 update(L,R,c,lson);51 if(m >1;61 long long ans=0;62 if(L<=m)63 ans+=query(L,R,lson);64 if(m
题意:
1 a:询问是不是有连续长度为a的空房间,有的话住进最左边
2 a b:将[a,a+b-1]的房间清空思路:记录区间中最长的空房间线段树操作:update:区间替换 query:询问满足条件的最左断点msum[i]表示以根结点编号为i的剩余最大房间数
lsum[i]表示前缀最大房间数
rsum[i]表示后缀最大房间数
col[i]为染色标记,-1表示初始,1表示有人住,0表示没有人主。
主要就区间合并的问题,将左半边的与右半边的合并
如果rsum[left son]+lsum[right son]>=d 那么就说明存在这样一个横跨两棵子树的区间,由于我们知道左子树最右边点的坐标x,那么这个连续空区间的第一个位置自然就是x-rsum[left son]+1。
1 #include2 #include 3 #include 4 #include 5 #define maxlen 60010 6 #define lson l,m,rt<<1 7 #define rson m+1,r,rt<<1|1 8 using namespace std; 9 int msum[maxlen<<2],lsum[maxlen<<2],rsum[maxlen<<2],col[maxlen<<2]; 10 int max(int a,int b) 11 { 12 return a>b?a:b; 13 } 14 void pushdown(int rt,int m) 15 { 16 if(col[rt]!=-1) 17 { 18 col[rt<<1]=col[rt<<1|1]=col[rt]; 19 msum[rt<<1]=lsum[rt<<1]=rsum[rt<<1]=col[rt]?0:m-(m>>1); 20 msum[rt<<1|1]=lsum[rt<<1|1]=rsum[rt<<1|1]=col[rt]?0:(m>>1); 21 col[rt]=-1; 22 } 23 } 24 void pushup(int rt,int m) 25 { 26 lsum[rt]=lsum[rt<<1]; 27 rsum[rt]=rsum[rt<<1|1]; 28 if(lsum[rt]==m-(m>>1)) 29 lsum[rt]+=lsum[rt<<1|1]; 30 if(rsum[rt]==(m>>1)) 31 rsum[rt]+=rsum[rt<<1]; 32 msum[rt]=max(lsum[rt<<1|1]+rsum[rt<<1],max(msum[rt<<1],msum[rt<<1|1])); 33 } 34 35 void build(int l,int r,int rt) 36 { 37 msum[rt]=lsum[rt]=rsum[rt]=r-l+1; 38 col[rt]=-1; 39 if(l==r) 40 return ; 41 int m=(l+r)>>1; 42 build(lson); 43 build(rson); 44 } 45 void update(int L,int R,int c,int l,int r,int rt) 46 { 47 if(L<=l&&r<=R) 48 { 49 msum[rt]=lsum[rt]=rsum[rt]=c?0:(r-l+1); 50 col[rt]=c; 51 return ; 52 } 53 pushdown(rt,r-l+1); 54 int m=(l+r)>>1; 55 if(L<=m) 56 update(L,R,c,lson); 57 if(m >1; 67 if(msum[rt<<1]>=sum) 68 return query(sum,lson); 69 else if(rsum[rt<<1]+lsum[rt<<1|1]>=sum) 70 return m-rsum[rt<<1]+1; 71 return query(sum,rson); 72 } 73 int main () 74 { 75 int n,m; 76 while(scanf("%d%d",&n,&m)!=EOF) 77 { 78 int cmd,x,y; 79 build(1,n,1); 80 while(m--) 81 { 82 scanf("%d",&cmd); 83 if(cmd==1) 84 { 85 scanf("%d",&x); 86 if(msum[1]
未完待续...