线段树、树状数组

A 树状数组:
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include <string.h>
 5 using namespace std;
 6 // 1h / 10min
 7 const int maxn = 32001;
 8 int c[maxn],ans[maxn]; // c[i] : 以i为横坐标的星星左侧和下侧星星的个数, ans[i] : 某层的星星的个数
 9 int x,y;
10 int lowbit(int x)
11 {
12 return x&(-x);
13 }
14 int sum(int x) // 计算横坐标为x的星星的level
15 {
16 int s = 0;
17 for (x ; x > 0 ; x -=lowbit(x))
18 {
19 s += c[x];
20 }
21 return s;
22 }
23 void insert(int x)
24 {
25 for (x ; x <= maxn ; x += lowbit(x))
26 {
27 c[x]++;
28 }
29 }
30 int main()
31 {
32 int n;
33 while (scanf("%d",&n)!=EOF)
34 {
35 memset(c,0,sizeof(c));
36 memset(ans,0,sizeof(ans));
37 for (int i=0;i<n;i++)
38 {
39 scanf("%d%d",&x,&y); x++;// 横坐标整体往右移动一位
40 ans[sum(x)]++;
41 insert(x);
42 }
43 for (int i=0;i<n;i++)
44 cout << ans[i] << endl;
45 }
46  
47 }

 

 
  • 有一点明白但还是不是很懂怎么转换的过程,再重新顺一遍过程。
线段树
  • 自学困难的知识的时候,不要有畏难心理。所有的难知识都可以通过一定的方法,掰碎了一点一点消化吸收的,我只需要一个程序一个程序地编写。在编写的过程中,遇到不清楚的知识点,一定要记下来!并且回去反复思考总结!我的问题总是反反复复的出现,一如我踩过的坑,下次遇到这个坑的时候还是义无反顾地跳进去,这就是我最大的问题!不总结!犯重复的错误!而且你要有时刻准备好的状态,不要畏畏缩缩的,大家都是这么过来的,慢慢来,还有两个月的时间,每天做五道题,你会超越绝大多数的人。
  • 创建线段树和中序遍历线段树的代码写不对,爆出segment fault这等高级段错误,与结构体、指针、构造函数有关。
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 using namespace std;
  5  
  6 struct line
  7 {
  8 int l,r,count;
  9 struct line *lchild,*rchild;
 10 line(int a,int b)
 11 {
 12 l = a;r = b;count = 0;
 13 lchild = NULL;
 14 rchild = NULL;
 15 }
 16 };
 17  
 18  
 19 const int maxn = 20;
 20 int a[maxn][2],p[maxn];
 21  
 22 /*
 23  
 24 指针一定要初始化了在使用!!!
 25 两种初始化创建的方法: 第一种一直调试不过原因:
 26 1. line中指针一定要初始化为NULL
 27 2. 传参数的时候要传引用。指针的引用。不然仅仅是复制一个指针并不是在原来的指针上改变。
 28 如果改变影响到它自身的话就用引用。
 29 */
 30 // 这个创建树也不太会写诶
 31 void createLine(line* &root,int left,int right)
 32 {
 33 root = new line(left,right);
 34 if (left < right) // l==r 叶节点时便不往下继续分了
 35 {
 36 int mid = (left+right)/2;
 37 createLine(root->lchild,left,mid);
 38 createLine(root->rchild,mid+1,right);
 39 }
 40 }
 41  
 42 /*
 43 void createLine(line *root) {
 44 int left = root->l;
 45 int right = root->r;
 46 if (left < right) {
 47 int mid = (left + right) / 2;
 48 line *lc = new line(left, mid);
 49 line *rc = new line(mid + 1, right);
 50 root->lchild = lc;
 51 root->rchild = rc;
 52 createLine(lc);
 53 createLine(rc);
 54 }
 55 }
 56 */
 57 void inOrder(line *root)
 58 {
 59  
 60 if ( root !=NULL)
 61 {
 62 inOrder(root->lchild);
 63 cout << "["<<root->l<<","<<root->r<<"]" << root->count<< endl;
 64 inOrder(root->rchild);
 65 }
 66 }
 67 void insertLine(line* &root,int l, int r )
 68 {
 69 // 把[l,r]插入到线段树中
 70 cout << root->l << "," << root->r << endl;
 71 if (root->l == l && root->r == r)
 72 {
 73 // 1. 刚好重合
 74 root->count ++;
 75 return ;
 76 }
 77 else
 78 {
 79 int mid = (root->l+root->r)/2; // 判断线段在root的左半区间还是右半区间
 80 if (r <= mid) // 左半区间
 81 {
 82 insertLine(root->lchild,l,r);
 83 }
 84 else if ( l > mid ) // 右半区间
 85 {
 86 insertLine(root->rchild,l,r);
 87 }
 88 else
 89 {
 90 // 从中间分开
 91 int mid_l = (l+r)/2; // 把小线段从中间分开而不是用root的区间 ? 用root的区间可以吗?
 92 insertLine(root->lchild,l,mid_l);
 93 insertLine(root->rchild,mid_l+1,r);
 94 }
 95 }
 96  
 97 }
 98 // 这个递归不太会写诶
 99 int getCount(line *root,int x)
100 { // 每一层的ans = 根的count + 左子树count + 右子树count
101 int ans = 0; // count 个数
102 int mid = (root->l+root->r)/2;
103 if (root->l <= x && root->r >= x) // 在区间范围内
104 ans += root->count;
105 if (root->l == root->r)
106 {
107 return ans;
108 }
109 if (x > mid)
110 {
111 ans += getCount(root->rchild,x);
112 }
113 else
114 {
115 ans +=getCount(root->lchild,x);
116 }
117 return ans;
118 }
119 int main()
120 {
121 int n,m;
122 // 给定N条线段 , M个点, 判断每个点在几个线段中出现过
123 while (cin >> n >> m)
124 {
125 // 输入线段和点
126 int l = 65515,r = -65515;
127 for (int i=0;i<n;i++)
128 {
129 cin >>a[i][0] >> a[i][1];
130 if (a[i][0] < l)
131 {
132 l = a[i][0];
133 }
134 if (a[i][1] > r)
135 {
136 r = a[i][1];
137 }
138 }
139 for (int i=0;i<m;i++) cin >> p[i];
140 // 创建线段树
141 // 1. 找到给定线段所覆盖的最大区间范围,创建以该范围为根节点的线段树
142 line *root = new line(l,r);
143 createLine(root,l,r);
144 // createLine(root);
145 // inOrder(root);
146 for (int i = 0;i<n;i++)
147 {
148 insertLine(root,a[i][0],a[i][1]); // 依次把各条线段插入线段树
149 }
150 for (int i=0;i<m;i++)
151 {
152 // 对于每一个点
153 cout << getCount(root,i)<< endl;
154 }
155 }
156 }

 

 
敌兵布阵
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include <string>
  5 #include <string.h>
  6 using namespace std;
  7 // 1h 35
  8 // (step<<1)+1 括号必须加,优先级问题
  9 const int maxn = 1000000; // 数组长度定义为多少??
 10 struct node
 11 {
 12 int left,right,value;
 13 }tree[maxn];
 14 /*
 15 void build(int l,int r,int step)
 16 {
 17 // 1. 第step个结点的赋值
 18 tree[step].left = l;
 19 tree[step].right = r;
 20 tree[step].value = 0;
 21 // cout << "结点:"<< step << ",左:" << l << ",右:" << r<< endl;
 22 // 2. 递归边界
 23 if (l == r)
 24 return ;
 25 // 3. 一分为2,继续递归创建子树
 26 int mid = (l+r)>>1; // 右移1位 = 除以2, 为什么不直接除以2 ??
 27 build(l,mid,step<<1); // step * 2 左子树
 28 build(mid+1,r,(step<<1)+1); // step * 2 + 1右子树
 29 }
 30 */
 31 void init(int n)//新建一个线段树
 32 {
 33 int i,k;
 34 for(k = 1; k<n; k<<=1);
 35 for(i = k; i<2*k; i++)
 36 {
 37 tree[i].left = tree[i].right = i-k+1;
 38 tree[i].value = 0;
 39 }
 40 for(i = k-1; i>0; i--)
 41 {
 42 tree[i].left = tree[2*i].left;
 43 tree[i].right = tree[2*i+1].right;
 44 tree[i].value = 0;
 45 }
 46 }
 47 void update(int l,int r,int value,int step)
 48 {
 49 tree[step].value += value;
 50 // cout <<"结点:"<< step << "加上" << value <<endl;
 51 // 递归边界
 52 if (tree[step].left == tree[step].right ) // 更新到叶子结点 , 为什么不能写成left = l && right = r ??
 53 {
 54 // tree[step].value = value;
 55 // cout << "step:"<< step << ",l:" << l << ",r:" << r<< value <<endl;
 56 return;
 57 }
 58 int mid = (tree[step].left + tree[step].right) >> 1;
 59 if (r <= mid)
 60 {
 61 // 线段在根节点的左半区间
 62 update(l,r,value,step<<1);
 63 }else if (l > mid) // 右半区间
 64 {
 65 update(l,r,value,(step<<1)+1);
 66 }else
 67 {
 68 // l,r 跨越了mid,分别更新
 69 update(l,mid,value,step<<1);
 70 update(mid+1,r,value,(step<<1)+1);
 71 }
 72 }
 73 int query(int l,int r,int step) // 求 l - r的和 ????
 74 {
 75 // if (tree[step].left == tree[step].right)
 76 if(l == tree[step].left && r == tree[step].right)
 77 {
 78 // printf("返节点%d的值%d\n",step,tree[step].value);
 79 return tree[step].value;
 80 }
 81 int mid = (tree[step].left + tree[step].right) >> 1;
 82 if(r <= mid)
 83 {
 84 // printf("结点:%d 查询%d到%d \n",step,l,r);
 85 return query(l, r, step<<1);
 86 }
 87 if(l > mid)
 88 {
 89 // printf("结点:%d 查询%d到%d \n",step,l,r);
 90 return query(l, r, (step<<1)+1);
 91 }
 92 else
 93 {
 94 // printf("结点:%d 查询%d到%d 和%d %d \n",step,l,mid,mid+1,r);
 95 return query(l, mid, step<<1) + query(mid+1, r, (step<<1)+1);
 96 }
 97 }
 98 int main()
 99 {
100 int t,n,ai,a,b;
101 char order[10];
102 scanf("%d",&t);
103 for (int j = 1;j<=t;j++)
104 {
105 scanf("%d",&n);
106 // build(1,n,1);
107 init(n);
108 // 依次更新每个营地的人数值
109 for (int i=1;i<=n;i++)
110 {
111 scanf("%d",&ai);
112 update(i,i,ai,1);
113 }
114 printf("Case %d:\n",j);
115 while (scanf("%s",order)!=EOF && strcmp(order,"End")!=0)
116 {
117 cin >> a >> b;
118 if (strcmp(order,"Add") == 0)
119 {
120 update(a,a,b,1);
121 }
122 if (strcmp(order,"Sub") == 0)
123 {
124 update(a,a,-b,1);
125 }
126 if (strcmp(order,"Query")==0)
127 {
128 cout << query(a,b,1) << endl;
129 }
130 }
131 }
132 }

 

 
  • 我他喵的研究了三个小时之后终于accept而且我好像看懂其中的原理了我很开心。Hhhhhh这大概就是计算机的女人绝不认输的精神吧!
  • Alt text
C I hate it
  • 改了改update和query
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include <string>
  5 #include <string.h>
  6 using namespace std;
  7 const int maxn = 1000000;
  8 struct node
  9 {
 10 int left,right,value;
 11 }tree[maxn];
 12  
 13 void build(int n)
 14 {
 15 int i,k;
 16 for (k=1; k < n ; k = k<<1); // k是超过n的最小2的倍数
 17 for (i = k;i < 2*k ; i++) // 完全二叉树
 18 {
 19 tree[i].left = tree[i].right = i - k + 1;
 20 tree[i].value = 0;
 21 // cout << "node : " << i << ",left:" << i - k + 1 << ",right :" << i-k+1 << endl;
 22 }
 23 for ( i = k-1 ;i>0;i--)
 24 {
 25 tree[i].left = tree[2*i].left;
 26 tree[i].right = tree[2*i+1].right;
 27 tree[i].value = 0;
 28 // cout << "node : " << i << ",left:" << tree[2*i].left << ",right :" << tree[2*i+1].right << endl;
 29 }
 30 }
 31 void update(int l,int r,int value,int step)
 32 { // tree[step]存储左子树和右子树中较大的数
 33 if (tree[step].left == tree[step].right)
 34 { // 叶子结点时赋值value
 35 tree[step].value = value;
 36 return ;
 37 }
 38 int mid = (tree[step].left + tree[step].right) >> 1;
 39 if (r <= mid)
 40 update(l,r,value,step<<1);
 41 else if (l>mid)
 42 update(l,r,value,(step<<1)+1);
 43 else
 44 {
 45 update(l,mid,value,step<<1);
 46 update(mid+1,r,value,(step<<1)+1);
 47 }
 48 tree[step].value = max(tree[step<<1].value,tree[(step<<1)+1].value);
 49 // cout << "node : " << step << ",left:" << tree[step<<1].value << ",right :" << tree[(step<<1)+1].value << endl;
 50 }
 51 int query(int l,int r,int step)
 52 {
 53 // 查询l - r 中的最大值
 54  
 55 if (tree[step].left == l && tree[step].right == r)
 56 return tree[step].value;
 57  
 58 int mid = (tree[step].left + tree[step].right) >> 1;
 59 if (r <= mid)
 60 return query(l,r,step<<1);
 61 else if (l>mid)
 62 return query(l,r,(step<<1)+1);
 63 else
 64 {
 65 return max(query(l,mid,step<<1),query(mid+1,r,(step<<1)+1));
 66 }
 67  
 68 }
 69 int main()
 70 {
 71 int n,m,grade,a,b;
 72 char cmd[3];
 73 while (scanf("%d%d",&n,&m)!=EOF)
 74 {
 75 // 1. 创建线索树
 76 build(n);
 77 // 2. 输入数据并更新线索树
 78 for (int i=1;i<=n;i++)
 79 {
 80 scanf("%d",&grade);
 81 update(i,i,grade,1);
 82 }
 83 // cout << tree[1].value << endl;
 84 // 3. m条操作依次执行
 85 for (int i=0;i<m;i++)
 86 {
 87  
 88 scanf("%s%d%d",cmd,&a,&b);
 89 if (cmd[0] == 'Q')
 90 {
 91 cout << query(a,b,1) << endl; // 查询a-b中的最大值
 92 }
 93 if (cmd[0] == 'U')
 94 {
 95 update(a,a,b,1);
 96 }
 97  
 98 }
 99 }
100 }

 

 
但是看到有在build部分修改函数的:
D just a hook
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include <string>
 5 #include <string.h>
 6 using namespace std;
 7  
 8 const int maxn = 1000000;
 9 struct node
10 {
11 int left,right,value;
12 }tree[maxn];
13  
14 void build(int n)
15 {
16 int i,k;
17 for (k=1;k<n;k=k<<1);
18 for (i=k;i<k*2;i++)
19 {
20 tree[i].left = tree[i].right = i-k+1;tree[i].value = 0;
21 // printf("结点:%d,左边:%d,右边:%d\n",i,i-k+1,i-k+1);
22 }
23 for (i=k-1;i>0;i--)
24 {
25 tree[i].left = tree[2*i].left;
26 tree[i].right = tree[2*i+1].right;
27 tree[i].value = 0;
28 // printf("结点:%d,左边:%d,右边:%d\n",i,tree[i<<1].left,tree[(i<<1)+1].right);
29 }
30 }
31  
32 void update(int l,int r,int value ,int step)
33 {
34 tree[step].value += value;
35 // printf("结点:%d,左边:%d,右边:%d,value:%d\n",step,tree[step].left,tree[step].right,tree[step].value);
36 if (tree[step].left == tree[step].right)
37 {
38 return ;
39 }
40 int mid = (tree[step].left+tree[step].right)>>1;
41 if (r <= mid)
42 update(l,r,value,step<<1);
43 else if (l>mid)
44 update(l,r,value,(step<<1)+1);
45 else
46 {
47 update(l,mid,value,step<<1);
48 update(mid+1,r,value,(step<<1)+1);
49 }
50 }
51 int main()
52 {
53 int t,n,q,a,b,c,x;
54 scanf("%d",&t);
55 for (int i = 1;i<=t;i++)
56 {
57 scanf("%d%d",&n,&q); // stick number & oper number
58 build(n);
59 for (int g=1;g<=n;g++)
60 update(g,g,1,1);
61  
62 for (x=1;x<n;x=x<<1);
63 for (int j=1;j<=q;j++)
64 {
65 scanf("%d%d%d",&a,&b,&c); // a-b 修改mental kind
66 for (int k = a;k<=b;k++)
67 update(k,k,c-tree[x+k-1].value,1);
68 }
69 printf("Case %d:The total value of the hook is %d\n",i,tree[1].value);
70  
71  
72 }
73  
74 }

 

 
  • 区间更新 = 循环+每个点的单点更新 = Time limited exceed

转载于:https://www.cnblogs.com/twomeng/p/9509823.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值