几道简单的线段树入门题 POJ3264&&POJ3468&&POJ2777
Time Limit: 5000MS | Memory Limit: 65536K | |
Total Submissions: 40687 | Accepted: 19137 | |
Case Time Limit: 2000MS |
Description
For the daily milking, Farmer John's N cows (1 ≤ N ≤ 50,000) always line up in the same order. One day Farmer John decides to organize a game of Ultimate Frisbee with some of the cows. To keep things simple, he will take a contiguous range of cows from the milking lineup to play the game. However, for all the cows to have fun they should not differ too much in height.
Farmer John has made a list of Q (1 ≤ Q ≤ 200,000) potential groups of cows and their heights (1 ≤ height ≤ 1,000,000). For each group, he wants your help to determine the difference in height between the shortest and the tallest cow in the group.
Input
Lines 2..N+1: Line i+1 contains a single integer that is the height of cow i
Lines N+2..N+Q+1: Two integers A and B (1 ≤ A ≤ B ≤ N), representing the range of cows from A to B inclusive.
Output
Sample Input
6 3 1 7 3 4 2 5 1 5 4 6 2 2
Sample Output
6 3 0
每一个点很明显存的就是最大值和最小值,现在看从这道题入手线段树是有些不好的,因为觉得这道题并没有展现出线段树的思想,只是套用了线段树的模式,然后最大值和最小值也很好表示,每一个点的最大值,来自于左右子树的最大值,最小值来自于左右子树的最小值。
然后这个查询,之前一直没注意第一个剪枝部分。
if (pRoot->nMin >= nMin&&pRoot->nMax <= nMax) return;
其实第一个剪枝部分使得性能大幅度提升,而且个人觉得这个才是些微展露了线段树强大的地方
这道题是线段树最简单的模板了。
代码:
#include <iostream> #include <algorithm> #include <cmath> #include <vector> #include <string> #include <cstring> #pragma warning(disable:4996) using namespace std; #define MY_MIN 99999999 #define MY_MAX -99999999 struct no { int L, R;//区间起点和终点 int nMin, nMax;//本区间的最小值和最大值 no *pLeft, *pRight; }; int nMax, nMin; no Tree[1000000]; int ncount = 0; void BuildTree(no *pRoot, int L, int R) { pRoot->L = L; pRoot->R = R; pRoot->nMin = MY_MIN; pRoot->nMax = MY_MAX; if (L != R) { ncount++; pRoot->pLeft = Tree + ncount; ncount++; pRoot->pRight = Tree + ncount; BuildTree(pRoot->pLeft, L, (L + R) / 2); BuildTree(pRoot->pRight, (L + R) / 2 + 1, R); } } void Insert(no *pRoot, int i, int v) { if (pRoot->L == i&&pRoot->R == i) { pRoot->nMin = pRoot->nMax = v; return; } pRoot->nMin = min(v, pRoot->nMin); pRoot->nMax = max(v, pRoot->nMax); if (i <= (pRoot->L + pRoot->R) / 2) { Insert(pRoot->pLeft, i, v); } else { Insert(pRoot->pRight, i, v); } } void Query(no * pRoot, int s, int e) { if (pRoot->nMin >= nMin&&pRoot->nMax <= nMax) return; if (s == pRoot->L&&e == pRoot->R) { nMin = min(pRoot->nMin, nMin); nMax = max(pRoot->nMax, nMax); return; } if (e <= (pRoot->L + pRoot->R) / 2) { Query(pRoot->pLeft,s,e); } else if (s >= (pRoot->L + pRoot->R) / 2 + 1) { Query(pRoot->pRight,s,e); } else { Query(pRoot->pLeft, s, (pRoot->L + pRoot->R) / 2); Query(pRoot->pRight, (pRoot->L + pRoot->R) / 2+1, e); } } int n, q; int main() { int i, h, s, e; scanf("%d%d", &n, &q); BuildTree(Tree, 1, n); ncount = 0; for (i = 1; i <= n; i++) { scanf("%d", &h); Insert(Tree, i, h); } for (i = 1; i <= q; i++) { scanf("%d%d", &s, &e); nMax = MY_MAX; nMin = MY_MIN; Query(Tree, s, e); printf("%d\n", nMax - nMin); } return 0; }
Time Limit: 5000MS | Memory Limit: 131072K | |
Total Submissions: 79715 | Accepted: 24583 | |
Case Time Limit: 2000MS |
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 5 1 2 3 4 5 6 7 8 9 10 Q 4 4 Q 1 10 Q 2 4 C 3 6 3 Q 2 4
Sample Output
4 55 9 15
Hint
这道题也是给出了一个N个数的序列,比之前难在不只是查询,还有动态的对数组一段元素的相加,第一次做的时候没有用inc,直接就是sum,然后每次相加都是搞到叶子节点,这样的话实际就是浪费了线段树节省时间的功能,就是线段树是属于不见棺材不落泪的类型,我只要恰好覆盖了你要求的区间,我就坚决不往下走了,除非你要查询叶子节点,否则我根据我上面点记录的信息就可以回答你了何必走到叶子节点呢?
说白了,这题inc起到的就是不用继续往下面走的作用。
代码:
#include <iostream> #include <algorithm> #include <cmath> #include <vector> #include <string> #include <cstring> #pragma warning(disable:4996) using namespace std; struct no { int L, R; long long sum; long long inc; no * pLeft; no * pRight; }Tree[200002]; int n, q; long long ncount; void BuildTree(no * pRoot, int L, int R) { pRoot->L = L; pRoot->R = R; pRoot->sum = 0; pRoot->inc = 0; if (L != R) { ncount++; pRoot->pLeft = Tree + ncount; ncount++; pRoot->pRight = Tree + ncount; BuildTree(pRoot->pLeft, L, (L + R) / 2); BuildTree(pRoot->pRight, (L + R) / 2 + 1, R); } } void Insert(no *pRoot, int i, long long h) { if (pRoot->L == i&&pRoot->R == i) { pRoot->sum = h; return; } pRoot->sum += h; if (i <= (pRoot->L + pRoot->R) / 2) { Insert(pRoot->pLeft, i, h); } else { Insert(pRoot->pRight, i, h); } } void Add(no *pRoot, int s, int e, long long val) { if (pRoot->L == s&&pRoot->R == e) { pRoot->inc += val; return; } pRoot->sum += val*(e - s + 1); if (e <= (pRoot->L + pRoot->R) / 2) { Add(pRoot->pLeft, s, e,val); } else if (s >= (pRoot->L + pRoot->R) / 2 + 1) { Add(pRoot->pRight, s, e,val); } else { Add(pRoot->pLeft, s, (pRoot->L + pRoot->R) / 2,val); Add(pRoot->pRight, (pRoot->L + pRoot->R) / 2 + 1, e,val); } } long long Query(no *pRoot, int s, int e) { if (pRoot->L == s&&pRoot->R == e) { return pRoot->sum + (pRoot->R - pRoot->L + 1)*pRoot->inc; } pRoot->sum = pRoot->sum + (pRoot->R - pRoot->L + 1)*pRoot->inc; Add(pRoot->pLeft, pRoot->L, (pRoot->L + pRoot->R) / 2, pRoot->inc); Add(pRoot->pRight, (pRoot->L + pRoot->R) / 2 + 1, pRoot->R, pRoot->inc); pRoot->inc = 0; if (e <= (pRoot->L + pRoot->R) / 2) { return Query(pRoot->pLeft, s, e); } else if (s >= (pRoot->L + pRoot->R) / 2 + 1) { return Query(pRoot->pRight, s, e); } else { return Query(pRoot->pLeft, s, (pRoot->L + pRoot->R) / 2) + Query(pRoot->pRight, (pRoot->L + pRoot->R) / 2 + 1, e); } } int main() { int i, h, s, e; long long val; char oper; scanf("%d%d", &n, &q); BuildTree(Tree, 1, n); ncount = 0; for (i = 1; i <= n; i++) { scanf("%d", &h); Insert(Tree, i, h); } for (i = 1; i <= q; i++) { cin >> oper; if (oper == 'Q') { cin >> s >> e; printf("%lld\n", Query(Tree, s, e)); } else if (oper == 'C') { cin >> s >> e >> val; Add(Tree, s, e, val); } } return 0; }
Time Limit: 1000MS | Memory Limit: 65536KB | 64bit IO Format: %I64d & %I64u |
Description
There is a very long board with length L centimeter, L is a positive integer, so we can evenly divide the board into L segments, and they are labeled by 1, 2, ... L from left to right, each is 1 centimeter long. Now we have to color the board - one segment with only one color. We can do following two operations on the board:
1. "C A B C" Color the board from segment A to segment B with color C.
2. "P A B" Output the number of different colors painted between segment A and segment B (including).
In our daily life, we have very few words to describe a color (red, green, blue, yellow…), so you may assume that the total number of different colors T is very small. To make it simple, we express the names of colors as color 1, color 2, ... color T. At the beginning, the board was painted in color 1. Now the rest of problem is left to your.
Input
Output
Sample Input
2 2 4 C 1 1 2 P 1 2 C 2 2 2 P 1 2
Sample Output
2 1
这题我终于感觉摸到了线段树的门。。。想了一个晚上之前金学长的代码里cover变量的作用。。。后来发现就是之前Add函数里面inc的作用,这两个本质是相同的。就是获得整块区间就将inc改为true,当要对其子节点操作的时候在现有节点的信息基础上对子节点进行修改。
线段树就是“当线段恰好覆盖一个节点的区间就直接对该节点操作而不再向下操作。绝对不能把区间内所有节点全部一代到底,到叶子节点。”金教授说的太好了。
代码:
#include <iostream> #include <algorithm> #include <cmath> #include <vector> #include <string> #include <cstring> #pragma warning(disable:4996) using namespace std; struct no { int L,R; int col; bool inc; }tree1[900000]; int L1,T,O; void buildtree(int root,int L,int R) { tree1[root].L=L; tree1[root].R=R; tree1[root].col=1; tree1[root].inc=false; if(L!=R) { buildtree((root<<1)+1,L,(L+R)/2); buildtree((root<<1)+2,(L+R)/2+1,R); } } void insert(int root,int L,int R,int color) { if(tree1[root].L==L&&tree1[root].R==R) { tree1[root].inc=true; tree1[root].col= (1<<(color-1)); return ; } if(tree1[root].inc) { tree1[root].inc=false; tree1[(root<<1)+1].col = tree1[root].col; tree1[(root<<1)+2].col = tree1[root].col; tree1[(root<<1)+1].inc = true; tree1[(root<<1)+2].inc = true; } int mid = (tree1[root].L + tree1[root].R)/2; if(R<=mid) { insert((root<<1)+1,L,R,color); } else if(L>=mid+1) { insert((root<<1)+2,L,R,color); } else { insert((root<<1)+1,L,mid,color); insert((root<<1)+2,mid+1,R,color); } tree1[root].col = tree1[(root<<1)+1].col | tree1[(root<<1)+2].col; } int query(int root,int L,int R) { if(tree1[root].inc==true ||tree1[root].L==L && tree1[root].R==R) { return tree1[root].col; } int mid = (tree1[root].L + tree1[root].R)/2; if(R<=mid) { return query((root<<1)+1,L,R); } else if(L>=mid+1) { return query((root<<1)+2,L,R); } else { return query((root<<1)+1,L,mid)|query((root<<1)+2,mid+1,R); } } void out(int x) { int cnt=0; while(x!=0) { cnt += (x&1); x=x>>1; } printf("%d\n",cnt); } int main() { //freopen("i.txt","r",stdin); //freopen("o.txt","w",stdout); int i,s,e,color_s; string op; scanf("%d%d%d",&L1,&T,&O); buildtree(0,1,L1); for(i=1;i<=O;i++) { cin>>op; if(op=="C") { scanf("%d%d%d",&s,&e,&color_s); insert(0,s,e,color_s); } else { scanf("%d%d",&s,&e); int res=query(0,s,e); out(res); } } //system("pause"); return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。