线段树

来源:互联网 发布:行车证制作软件 编辑:程序博客网 时间:2024/06/14 13:18

第一类 区间查询与区间求和


F. Building Numbers
time limit per test
3.0 s
memory limit per test
256 MB
input
standard input
output
standard output

In this problem, you can build a new number starting from 1, by performing the following operations as much as you need:

  • Add 1 to the current number.
  • Add the current number to itself (i.e. multiply it by 2).

For example, you can build number 8 starting from 1 with three operations . Also, you can build number 10 starting from 1 with five operations .

You are given an array a consisting of n integers, and q queries. Each query consisting of two integers l and r, such that the answer of each query is the total number of operations you need to preform to build all the numbers in the range from l to r (inclusive) from array asuch that each number ai (l ≤ i ≤ r) will be built with the minimum number of operations.

Input

The first line contains an integer T (1 ≤ T ≤ 50), where T is the number of test cases.

The first line of each test case contains two integers n and q (1 ≤ n, q ≤ 105), where n is the size of the given array, and q is the number of queries.

The second line of each test case contains n integers a1, a2, ..., an (1 ≤ ai ≤ 1018), giving the array a.

Then q lines follow, each line contains two integers l and r (1 ≤ l ≤ r ≤ n), giving the queries.

Output

For each query, print a single line containing its answer.

Example
input
15 34 7 11 8 104 51 53 3
output
7185
Note

As input/output can reach huge size it is recommended to use fast input/output methods: for example, prefer to use scanf/printfinstead of cin/cout in C++, prefer to use BufferedReader/PrintWriter instead of Scanner/System.out in Java.

In the first query, you need 3 operations to build number 8, and 4 operations to build number 10. So, the total number of operations is 7.


题意:给出一个数字,构成它的方法有两种,初始值是1,可以在这基础上自加1,或者加上它本身,然后进行区间查询


思路:对于一个奇数,可以先自减1变成偶数后,得到可以减其自身的一个数,算出步骤数

然后用线段树进行区间查询


#include<iostream>#include<cstdio>using namespace std;#define ll long longconst int maxn=100005;ll sum[maxn<<2],a[maxn];int getN(ll temp){    int ans=0;    while(temp!=0){        if(temp%2==1){            temp--;        }else            temp/=2;        ans++;    }    return ans-1;}void pushup(int rt)//回溯(节点为两个子树之和){    sum[rt]=sum[rt<<1]+sum[rt<<1|1];}/*build函数是从底层建树,一直回溯到根节点,自己推导可以从根节点依次二分展开整个树*/void build(int l,int r,int rt){//[l,r]表示当前节点区间,rt表示当前节点的实际存储位置    if(l==r){//若到达叶节点        sum[rt]=a[l];        return;    }    int m=(l+r)>>1;    //左右递归    build(l,m,rt<<1);    build(m+1,r,rt<<1|1);    pushup(rt);}void update(int L,int C,int l,int r,int rt){//[l,r]表示当前区间,rt是当前节点编号    if(l==r){//到达叶节点,修改叶节点的值        sum[rt]=C;        return;    }    int m=(l+r)>>1;    //根据条件判断往左子树调用还是右    if(L<=m)        update(L,C,l,m,rt<<1);    else        update(L,C,m+1,r,rt<<1|1);    pushup(rt);//子节点的信息更新了,所以本节点也要更新信息}int query(int L,int R,int l,int r,int rt){//[L,R]表示要获取区间,[l,r]表示当前区间,rt:当前节点编号    if(L<=l&&r<=R){ //在区间内直接返回        return sum[rt];    }    int m=(l+r)>>1;    //左子区间:[l,m] 右子区间:[m+1,r]  求和区间:[L,R]    //累加答案    int ans=0;    if(L<=m){        int temp=query(L,R,l,m,rt<<1);//左子区间与[L,R]有重叠,递归        ans=temp+ans;    }    if(R>m){        int temp=query(L,R,m+1,r,rt<<1|1);//右子区间与[L,R]有重叠,递归        ans=temp+ans;    }    return ans;}int main(){//    freopen("in.txt","r",stdin);    int i,x,y;    int m,n,T,q;    ll temp;    scanf("%d",&T);    while(T--)    {        scanf("%d%d",&n,&q);        for(i=1;i<=n;i++){            scanf("%lld",&temp);            a[i]=getN(temp);//            cout<<a[i]<<endl;        }        build(1,n,1);//开始建树        while(q--)        {            scanf("%d%d",&x,&y);            printf("%d\n",query(x,y,1,n,1));        }    }}

第二类 求矩形并的面积


Atlantis
Time Limit: 1000MS Memory Limit: 10000KTotal Submissions: 23745 Accepted: 8829

Description

There are several ancient Greek texts that contain descriptions of the fabled island Atlantis. Some of these texts even include maps of parts of the island. But unfortunately, these maps describe different regions of Atlantis. Your friend Bill has to know the total area for which maps exist. You (unwisely) volunteered to write a program that calculates this quantity.

Input

The input consists of several test cases. Each test case starts with a line containing a single integer n (1 <= n <= 100) of available maps. The n following lines describe one map each. Each of these lines contains four numbers x1;y1;x2;y2 (0 <= x1 < x2 <= 100000;0 <= y1 < y2 <= 100000), not necessarily integers. The values (x1; y1) and (x2;y2) are the coordinates of the top-left resp. bottom-right corner of the mapped area. 
The input file is terminated by a line containing a single 0. Don't process it.

Output

For each test case, your program should output one section. The first line of each section must be "Test case #k", where k is the number of the test case (starting with 1). The second one must be "Total explored area: a", where a is the total explored area (i.e. the area of the union of all rectangles in this test case), printed exact to two digits to the right of the decimal point. 
Output a blank line after each test case.

Sample Input

210 10 20 2015 15 25 25.50

Sample Output

Test case #1Total explored area: 180.00 

Source

Mid-Central European Regional Contest 2000

[Submit]   [Go Back]   [Status]   [Discuss]


/* *  本题中的坐标是浮点类型的, 故不能将坐标直接离散.我们必须为它们建立一个对应关系, *  用一个整数去对应一个浮点数这样的对应关系在本题的数组y[]中。 */struct node{    int st, ed, c;      //  c: 区间被覆盖的层数, m: 区间的测度    double m;} ST[802];struct line{    double x, y1, y2;   //  纵方向直线, x:直线横坐标, y1 y2:直线上的下面与上面的两个纵坐标    bool s;             //  s = 1 : 直线为矩形的左边, s = 0:直线为矩形的右边} Line[205];double y[205], ty[205]; //  y[]整数与浮点数的对应数组; ty[]:用来求y[]的辅助数组void build(int root, int st, int ed){    ST[root].st = st;    ST[root].ed = ed;    ST[root].c = 0;    ST[root].m = 0;    if (ed - st > 1)    {        int mid = (st + ed) / 2;        build(root * 2, st, mid);        build(root * 2 + 1, mid, ed);    }    return ;}void updata(int root){    if (ST[root].c > 0)    {   //  将线段树上区间的端点分别映射到y[]数组所对应的浮点数上,由此计算出测度        ST[root].m = y[ST[root].ed - 1] - y[ST[root].st - 1];    }    else if (ST[root].ed - ST[root].st == 1)    {        ST[root].m = 0;    }    else    {        ST[root].m = ST[root * 2].m + ST[root * 2 + 1].m;    }    return ;}void insert(int root, int st, int ed){    if (st <= ST[root].st && ST[root].ed <= ed)    {        ST[root].c++;        updata(root);        return ;    }    if (ST[root].ed - ST[root].st == 1)    {        return ;    //  不出错的话 这句话就是冗余的    }    int mid = (ST[root].ed + ST[root].st) / 2;    if (st < mid)    {        insert(root * 2, st, ed);    }    if (ed > mid)    {        insert(root * 2 + 1, st, ed);    }    updata(root);    return ;}void Delete(int root, int st, int ed){    if (st <= ST[root].st && ST[root].ed <= ed)    {        ST[root].c--;        updata(root);        return ;    }    if (ST[root].ed - ST[root].st == 1)    {        return ;    //  不出错的话 这句话就是冗余的    }    int mid = (ST[root].st + ST[root].ed) / 2;    if (st < mid)    {        Delete(root * 2, st, ed);    }    if (ed > mid)    {        Delete(root * 2 + 1, st, ed);    }    updata(root);    return ;}int Correspond(int n, double t){    //  二分查找出浮点数t在数组y[]中的位置(此即所谓的映射关系)    int low, high, mid;    low = 0;    high = n - 1;    while (low < high)    {        mid = (low + high) / 2;        if (t > y[mid])        {            low = mid + 1;        }        else        {            high = mid;        }    }    return high + 1;}bool cmp(line l1, line l2){    return l1.x < l2.x;}int main(){    int n, i, num, l, r, c = 0;    double area, x1, x2, y1, y2;    while (cin >> n, n)    {        for (i = 0; i < n; i++)        {            cin >> x1 >> y1 >> x2 >> y2;            Line[2 * i].x = x1;            Line[2 * i].y1 = y1;            Line[2 * i].y2 = y2;            Line[2 * i].s = 1;            Line[2 * i + 1].x = x2;            Line[2 * i + 1].y1 = y1;            Line[2 * i + 1].y2 = y2;            Line[2 * i + 1].s = 0;            ty[2 * i] = y1;            ty[2 * i + 1] = y2;        }        n <<= 1;        sort(Line, Line + n, cmp);        sort(ty, ty + n);        y[0] = ty[0];        //  处理数组ty[]使之不含重覆元素,得到新的数组存放到数组y[]中        for (i = num = 1; i < n; i++)        {            if (ty[i] != ty[i - 1])            {                y[num++] = ty[i];            }        }        build(1, 1, num);   //  树的叶子节点与数组y[]中的元素个数相同,以便建立一一对应的关系        area = 0;        for (i = 0; i < n - 1; i++)        {   //  由对应关系计算出线段两端在树中的位置            l = Correspond(num, Line[i].y1);            r = Correspond(num, Line[i].y2);            if (Line[i].s)  //  插入矩形的左边            {                insert(1, l, r);            }            else            //  删除矩形的右边            {                Delete(1, l, r);            }            area += ST[1].m * (Line[i + 1].x - Line[i].x);        }        cout << "Test case #" << ++c << endl << "Total explored area: ";        cout << fixed << setprecision(2) << area << endl << endl;   //  需要引入iomanip头文件    }    return 0;}



第三类  求矩形并的周长(线段树+离散化+扫描线)



Picture
Time Limit: 2000MS Memory Limit: 10000KTotal Submissions: 12893 Accepted: 6810

Description

A number of rectangular posters, photographs and other pictures of the same shape are pasted on a wall. Their sides are all vertical or horizontal. Each rectangle can be partially or totally covered by the others. The length of the boundary of the union of all rectangles is called the perimeter. 

Write a program to calculate the perimeter. An example with 7 rectangles is shown in Figure 1. 

The corresponding boundary is the whole set of line segments drawn in Figure 2. 

The vertices of all rectangles have integer coordinates. 

Input

Your program is to read from standard input. The first line contains the number of rectangles pasted on the wall. In each of the subsequent lines, one can find the integer coordinates of the lower left vertex and the upper right vertex of each rectangle. The values of those coordinates are given as ordered pairs consisting of an x-coordinate followed by a y-coordinate. 

0 <= number of rectangles < 5000 
All coordinates are in the range [-10000,10000] and any existing rectangle has a positive area.

Output

Your program is to write to standard output. The output must contain a single line with a non-negative integer which corresponds to the perimeter for the input rectangles.

Sample Input

7-15 0 5 10-5 8 20 2515 -4 24 140 -6 16 42 15 10 2230 10 36 2034 0 40 16

Sample Output

228

Source

IOI 1998

[Submit]   [Go Back]   [Status]   [Discuss]



struct node{    int st, ed, m, lbd, rbd;    int sequence_line, count;} ST[40005];void build(int st, int ed, int v)       //  建树,区间为[st, ed]{    ST[v].st = st;    ST[v].ed = ed;    ST[v].m = ST[v].lbd = ST[v].rbd = 0;    ST[v].sequence_line = ST[v].count = 0;    if (ed - st > 1)    {        int mid = (st + ed) / 2;        build(st, mid, 2 * v + 1);        build(mid, ed, 2 * v + 2);    }    return ;}void UpData(int v)                      //  更新结点区间的测度{    if (ST[v].count > 0)    {        ST[v].m = ST[v].ed - ST[v].st;        ST[v].lbd = ST[v].rbd = 1;        ST[v].sequence_line = 1;        return ;    }    if (ST[v].ed - ST[v].st == 1)    {        ST[v].m = 0;        ST[v].lbd = ST[v].rbd = 0;        ST[v].sequence_line = 0;    }    else    {        int left = 2 * v + 1, right = 2 * v + 2;        ST[v].m = ST[left].m + ST[right].m;        ST[v].sequence_line = ST[left].sequence_line + ST[right].sequence_line - (ST[left].rbd & ST[right].lbd);        ST[v].lbd = ST[left].lbd;        ST[v].rbd = ST[right].rbd;    }    return ;}void insert(int st, int ed, int v){    if (st <= ST[v].st && ed >= ST[v].ed)    {        ST[v].count++;        UpData(v);        return ;    }    int mid = (ST[v].st + ST[v].ed) / 2;    if (st < mid)    {        insert(st, ed, 2 * v + 1);    }    if (ed > mid)    {        insert(st, ed, 2 * v + 2);    }    UpData(v);    return ;}void Delete(int st, int ed, int v){    if (st <= ST[v].st && ed >= ST[v].ed)    {        ST[v].count--;        UpData(v);        return ;    }    int mid = (ST[v].st + ST[v].ed) / 2;    if (st < mid)    {        Delete(st, ed, 2 * v + 1);    }    if (ed > mid)    {        Delete(st, ed, 2 * v + 2);    }    UpData(v);    return ;}struct line{    int x, y1, y2;  //  y1 < y2    bool d;         //  d=true表示该线段为矩形左边,d=false表示该线段为矩形的右边} a[10003];bool cmp(line t1, line t2)  //  为线段排序的函数,方便从左向右的扫描{    return t1.x < t2.x;}void cal_C(int n);int main(){    int n, x1, x2, y1, y2, i, j, suby, upy;    while (scanf("%d",&n) != EOF)    {        j = 0;        suby = 10000;        upy = -10000;        for (i = 0; i < n; i++)        {            scanf("%d%d%d%d", &x1, &y1, &x2, &y2);            a[j].x = x1;            a[j].y1 = y1;            a[j].y2 = y2;            a[j].d = 1;            j++;            a[j].x = x2;            a[j].y1 = y1;            a[j].y2 = y2;            a[j].d = 0;            j++;            if (suby > y1)            {                suby = y1;            }            if (upy < y2)            {                upy = y2;            }        }        sort(a, a + j, cmp);        build(suby, upy, 0);        cal_C(j);    }    return 0;}void cal_C(int n){    int i, t2, sum = 0;    t2 = 0;    a[n] = a[n - 1];    for (i = 0; i < n; i++)    {        if (a[i].d == 1)        {            insert(a[i].y1, a[i].y2, 0);        }        else        {            Delete(a[i].y1, a[i].y2, 0);        }        sum += ST[0].sequence_line * (a[i + 1].x - a[i].x) * 2;        sum += abs(ST[0].m - t2);        t2 = ST[0].m;    }    printf("%d\n", sum);}










原创粉丝点击