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 }
树状数组:
https://www.cnblogs.com/hsd-/p/6139376.html
https://blog.csdn.net/flushhip/article/details/79165701
https://blog.csdn.net/zheng0518/article/details/51119042
https://blog.csdn.net/kkkkahlua/article/details/76785265
https://blog.csdn.net/moep0/article/details/52770728
题目解析:https://blog.csdn.net/zhanghaoxian1/article/details/74275951
- 有一点明白但还是不是很懂怎么转换的过程,再重新顺一遍过程。
线段树
- 自学困难的知识的时候,不要有畏难心理。所有的难知识都可以通过一定的方法,掰碎了一点一点消化吸收的,我只需要一个程序一个程序地编写。在编写的过程中,遇到不清楚的知识点,一定要记下来!并且回去反复思考总结!我的问题总是反反复复的出现,一如我踩过的坑,下次遇到这个坑的时候还是义无反顾地跳进去,这就是我最大的问题!不总结!犯重复的错误!而且你要有时刻准备好的状态,不要畏畏缩缩的,大家都是这么过来的,慢慢来,还有两个月的时间,每天做五道题,你会超越绝大多数的人。
- 创建线段树和中序遍历线段树的代码写不对,爆出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这大概就是计算机的女人绝不认输的精神吧!
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