线段树经典类型归纳

来源:互联网 发布:中银淘宝校园卡申请 编辑:程序博客网 时间:2024/06/05 03:17

第一道:

HDU 1754 单点更新,区间查询最大值,水题……

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<map>#include<queue>#include<set>#include<cmath>#include<bitset>#define mem(a,b) memset(a,b,sizeof(a))#define lson i<<1,l,mid#define rson i<<1|1,mid+1,r#define INF 1000000#define maxn 1000010using namespace std;typedef long long ll;typedef unsigned long long ull;int Max[maxn],a[200005],n,m,i;void pushup(int i){    Max[i]=max(Max[i<<1],Max[i<<1|1]);}void build(int i,int l,int r){    if(l==r)    {        Max[i]=a[l];        return ;    }    int mid=(l+r)>>1;    build(lson);build(rson);    pushup(i);}void update(int i,int l,int r,int x,int v){    if(l==r&&l==x)    {        Max[i]=v;        return ;    }    int mid=(l+r)>>1;    if(x<=mid) update(lson,x,v);    else update(rson,x,v);    pushup(i);}int query(int i,int l,int r,int L,int R){    if(L<=l&&r<=R) return Max[i];    int mid=(l+r)>>1,ans=0;    if(L<=mid) ans=max(ans,query(lson,L,R));    if(R>mid) ans=max(ans,query(rson,L,R));    return ans;}int main(){    while(~scanf("%d%d",&n,&m))    {        for(i=1;i<=n;i++)            scanf("%d",a+i);        build(1,1,n);        int x,y;        char str[3];        while(m--)        {            scanf("%s%d%d",str,&x,&y);            if(str[0]=='Q')                printf("%d\n",query(1,1,n,x,y));            else update(1,1,n,x,y);        }    }    return 0;}

第二道:

HDU 1698 区间更新,区间查询,lazy标记种类,水……

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<map>#include<queue>#include<set>#include<cmath>#include<bitset>#define mem(a,b) memset(a,b,sizeof(a))#define lson i<<1,l,mid#define rson i<<1|1,mid+1,r#define INF 1000000#define maxn 400010using namespace std;typedef long long ll;typedef unsigned long long ull;int sum[maxn],lazy[maxn],n,q;void pushup(int i){    sum[i]=sum[i<<1]+sum[i<<1|1];}void pushdown(int i,int l,int r){    if(lazy[i])    {        int mid=(l+r)>>1;        lazy[i<<1]=lazy[i<<1|1]=lazy[i];        sum[i<<1]=(mid-l+1)*lazy[i<<1];        sum[i<<1|1]=(r-mid)*lazy[i<<1|1];        lazy[i]=0;    }}void update(int i,int l,int r,int L,int R,int v){    if(l==L&&r==R)    {        sum[i]=(r-l+1)*v;        lazy[i]=v;        return ;    }    int mid=(l+r)>>1;    pushdown(i,l,r);    if(R<=mid) update(lson,L,R,v);    else if(L>mid) update(rson,L,R,v);    else    {        update(lson,L,mid,v);        update(rson,mid+1,R,v);    }    pushup(i);}void build(int i,int l,int r){    if(l==r)    {        sum[i]=1;        lazy[i]=1;        return ;    }    sum[i]=lazy[i]=0;    int mid=(l+r)>>1;    build(lson);build(rson);    pushup(i);}int main(){    int t;    cin>>t;    for(int i=1;i<=t;i++)    {        scanf("%d%d",&n,&q);        build(1,1,n);        int l,r,v;        while(q--)        {            scanf("%d%d%d",&l,&r,&v);            update(1,1,n,l,r,v);        }        printf("Case %d: The total value of the hook is %d.\n",i,sum[1]);    }    return 0;}

第三道:

HDU 1394 单点更新,区间查询,线段树、树状数组、归并排序求逆序数。

解法一:线段树

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<map>#include<queue>#include<set>#include<cmath>#include<bitset>#define mem(a,b) memset(a,b,sizeof(a))#define lson i<<1,l,mid#define rson i<<1|1,mid+1,r#define INF 1000000007#define maxn 400010using namespace std;typedef long long ll;typedef unsigned long long ull;int sum[20005],a[5005];void update(int i,int l,int r,int x,int v){    sum[i]+=v;    if(l==r) return ;    int mid=(l+r)>>1;    if(x<=mid) update(lson,x,v);    else update(rson,x,v);}int query(int i,int l,int r,int L,int R){    if(l==L&&r==R) return sum[i];    int mid=(l+r)>>1;    if(R<=mid) return query(lson,L,R);    else if(L>mid) return query(rson,L,R);    else return query(lson,L,mid)+query(rson,mid+1,R);}int main(){    int n,i;    while(~scanf("%d",&n))    {        int ans=0,Min=INF;        mem(sum,0);        for(i=1;i<=n;i++)        {            scanf("%d",a+i),a[i]++;            ans+=query(1,1,n,a[i],n);            update(1,1,n,a[i],1);        }        Min=min(Min,ans);        for(i=1;i<=n;i++)        {            ans-=a[i]-1;            ans+=n-a[i];            Min=min(Min,ans);        }        printf("%d\n",Min);    }    return 0;}

解法二:树状数组:

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<map>#include<queue>#include<set>#include<cmath>#include<bitset>#define mem(a,b) memset(a,b,sizeof(a))#define lson i<<1,l,mid#define rson i<<1|1,mid+1,r#define INF 1000000007#define maxn 400010using namespace std;typedef long long ll;typedef unsigned long long ull;int n,c[maxn],a[maxn];void update(int x){    for(int i=x;i;c[i]+=1,i-=i&(-i));    //更新x之前的,这样查询x+1之后的数才会查询不到逆序数}int query(int x){    int i,sum=0;    for(i=x;i<=n;sum+=c[i],i+=i&(-i));    return sum;}int main (){    while(~scanf("%d",&n))    {        int i,sum=0,Min=INF;        mem(c,0);        for(i=1;i<=n;i++)        {            scanf("%d",a+i),a[i]++;            sum+=query(a[i]);            update(a[i]);        }        Min=min(sum,Min);        for(i=1;i<=n;i++)        {            sum-=a[i]-1;            sum+=n-a[i];            Min=min(sum,Min);        }        printf("%d\n",Min);    }    return 0;}

第四道:

POJ 2528 线段树+离散化

这题T了几个小时了,晕死了……原来是在离散化的时候多了点时间,尼玛,让我debug了两个小时!!!!

其实我这样离散化还有个bug,因为单点离散化和区间离散化是不一样的。

比如这个例子:

3

1 10

1 3

6 10

我这个代码就输出2,本来是3的,POJ 的数据弱了。

不过改一点就可以过了,离散化的时候可以离散成:Map[c[i]]=(i+1)*2,然后查询的时候,总区间也*2就行了,这样在区间与区间之间就可以查询得到了。懒得改了,想做好的就按这样做吧。

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<map>#include<queue>#include<set>#include<cmath>#include<bitset>#define mem(a,b) memset(a,b,sizeof(a))#define lson i<<1,l,mid#define rson i<<1|1,mid+1,r#define INF 10000010#define maxn 80010using namespace std;typedef long long ll;typedef unsigned long long ull;int lazy[maxn];int a[10005],b[10005],c[20010],num[20010];set<int>s;void pushdown(int i,int v){    if(lazy[i]&&lazy[i]!=v)    {        lazy[i<<1]=lazy[i<<1|1]=lazy[i];        lazy[i]=0;    }}void update(int i,int l,int r,int L,int R,int v){    if(l==L&&r==R)    {        lazy[i]=v;        return ;    }    int mid=(l+r)>>1;    pushdown(i,v);    if(R<=mid) update(lson,L,R,v);    else if(L>mid) update(rson,L,R,v);    else    {        update(lson,L,mid,v);        update(rson,mid+1,R,v);    }}void query(int i,int l,int r){    if(lazy[i])    {        s.insert(lazy[i]);        return ;    }    if(l==r||lazy[i]==-1) return ;    int mid=(l+r)>>1;    query(lson);query(rson);}map<int,int>Map;int main(){    int t;    scanf("%d",&t);    while(t--)    {        int i,n,k=0;        Map.clear();        s.clear();        mem(lazy,0);        scanf("%d",&n);        for(i=0;i<n;i++)        {            scanf("%d%d",&a[i],&b[i]);            c[k++]=a[i];            c[k++]=b[i];        }        sort(c,c+k);        k=unique(c,c+k)-c;        for(i=0;i<k;i++)            Map[c[i]]=i+1;        for(i=0;i<n;i++)            update(1,1,k,Map[a[i]],Map[b[i]],i+1);        query(1,1,k);        printf("%d\n",s.size());    }    return 0;}


第五道:

HDU 2795 几何中的单点更新

思路:刚开始看这题的时候也不知道该怎么用线段树做,想了好久也不知道,然后看了下别人的解题报告才知道这么容易,只是想不到啊!用叶结点表示长方形的行,然后叶结点的域表示的是当前行的宽(或者还剩多少可以容纳的宽度),这样就可以轻松解决了。线段树不建太长,有多少行就建多少,如果n大的话,就建n的长度就行了,这点看刚才看题的时候10^9就怕了,所以就没想到这么机智的做法。

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<map>#include<queue>#include<set>#include<cmath>#include<bitset>#define mem(a,b) memset(a,b,sizeof(a))#define lson i<<1,l,mid#define rson i<<1|1,mid+1,r#define INF 10000010#define maxn 800010using namespace std;typedef long long ll;typedef unsigned long long ull;int Max[maxn];void pushup(int i){    Max[i]=max(Max[i<<1],Max[i<<1|1]);}void update(int i,int l,int r,int v){    if(l==r)    {        printf("%d\n",l);        Max[i]-=v;        return ;    }    int mid=(l+r)>>1;    if(Max[i<<1]>=v) update(lson,v);    else update(rson,v);//用域比较v    pushup(i);}int main(){    int h,w,n;    while(~scanf("%d%d%d",&h,&w,&n))    {        int i,len=h<n?h:n;        for(int i=0;i<len*4+10;i++)            Max[i]=w;        while(n--)        {            int v;            scanf("%d",&v);            if(Max[1]<v) puts("-1");            else update(1,1,len,v);        }    }    return 0;}

第六道:

POJ 3667  区间合并,设置左右中区间,然后更新的时候合并。刚开始想的时候确实没想到这么机智的做法,我想的做法是加个lazy标记,然后查询的时候再加个flag标记,初始flag=0,如果找到flag=1,然后如果=1的话,递归就不进行下去了, 这样应该也可以做这道题,不过可能代码比较长忽悠一点吧……

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<map>#include<queue>#include<set>#include<cmath>#include<bitset>#define mem(a,b) memset(a,b,sizeof(a))#define lson i<<1,l,mid#define rson i<<1|1,mid+1,r#define INF 10000010#define maxn 200010using namespace std;typedef long long ll;typedef unsigned long long ull;int sum[maxn],lsum[maxn],rsum[maxn],lazy[maxn];void pushdown(int i,int v){    if(lazy[i]!=-1)    {        int l=i<<1,r=i<<1|1;        lazy[l]=lazy[r]=lazy[i];        sum[l]=lsum[l]=rsum[l]=(lazy[i]?0:(v-(v>>1)));//lazy为1表清空房间        sum[r]=lsum[r]=rsum[r]=(lazy[i]?0:(v>>1));//右孩子值        lazy[i]=-1;    }}void pushup(int i,int v){    int l=i<<1,r=i<<1|1;    lsum[i]=lsum[l];    if(lsum[l]==v-(v>>1))//若左子树全空        lsum[i]+=lsum[r];//则加上右子树的左区间    rsum[i]=rsum[r];    if(rsum[r]==v>>1)        rsum[i]+=rsum[l];    sum[i]=max(max(sum[l],sum[r]),rsum[l]+lsum[r]);}void update(int i,int l,int r,int L,int R,int v){    if(L<=l&&r<=R)    {        sum[i]=lsum[i]=rsum[i]=(v?0:r-l+1);        lazy[i]=v;        return ;    }    pushdown(i,r-l+1);    int mid=(l+r)>>1;    if(L<=mid) update(lson,L,R,v);    if(mid<R) update(rson,L,R,v);    pushup(i,r-l+1);}int query(int i,int l,int r,int v){    if(l==r) return l;    pushdown(i,r-l+1);    int mid=(l+r)>>1;    if(sum[i<<1]>=v) return query(lson,v);    else if(rsum[i<<1]+lsum[i<<1|1]>=v)        return mid-rsum[i<<1]+1;    return query(rson,v);}void build(int i,int l,int r){    sum[i]=lsum[i]=rsum[i]=r-l+1;    lazy[i]=-1;    if(l==r) return ;    int mid=(l+r)>>1;    build(lson);build(rson);}int main(){    //freopen("test.txt","r",stdin);    int n,m;    scanf("%d%d",&n,&m);    build(1,1,n);    while(m--)    {        int q,l,r;        scanf("%d%d",&q,&l);        if(q==1)        {            if(sum[1]<l) {puts("0");continue;}            int k=query(1,1,n,l);            printf("%d\n",k);            update(1,1,n,k,k+l-1,1);//住房        }        else        {            scanf("%d",&r);            update(1,1,n,l,l+r-1,0);        }    }    return 0;}


第七道:

POJ 2892  & HDU 1540

解法一:树状数组查找第k小的数的下标。

树状数组查找第k小的数做的,挺机智的。

查找第k小的数,这个知识学习了一下午才知道什么意思,尼玛……其实意思就是查找值为k的数在树状数组中其和从1开始到哪个下标其和等于k。比如树状数组中的:c[1]=1,c[2]=1,c[3]=0,c[4]=1,c[5]=0,c[6]=0,c[7]=1,c[8]=2,c[9]=1.

则sum[5]=1,现在查找k=1的下标,那就是4;再比如sum[8]=2,查找k=2的下标就是8,这个求法可以用下面代码的find_kth函数二分逼近法求出。

但是在HDU 1540上过不了,所说是数据水了。改了一上午还是过不了,还以为是数据有错了,然后拿别人代码交了一发线段树就过了,难道这题限制用树状数组?算了,还是用线段树写发区间合并吧!

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<map>#include<queue>#include<set>#include<stack>#include<cmath>#include<bitset>#define mem(a,b) memset(a,b,sizeof(a))#define lowbit(x) (x&-x)#define INF 1000000#define maxn 100010using namespace std;typedef long long ll;typedef unsigned long long ull;int c[50010];void update(int x,int n,int val){    for(; x<=n; x+=lowbit(x))        c[x]+=val;}int sum(int x,int n){    int ans=0;    for(; x>0; x-=lowbit(x))        ans+=c[x];    return ans;}int find_kth(int k,int n,int logn){    int cur=0,cnt=0,i;    for(i=logn; i>=0; i--)    {        cur+=1<<i;        if(cur>n||cnt+c[cur]>=k) cur-=1<<i;        else cnt+=c[cur];    }    return cur+1;}int main(void){    int n,m,x;    char s[2];    scanf("%d%d",&n,&m);    stack<int>st;    n+=2;    int logn=log(n+1.0)/log(2.0);    update(1,n,1);    update(n,n,1);    while(m--)    {        scanf("%s",s);        if(s[0]=='D')        {            scanf("%d",&x);            x++;            update(x,n,1);            st.push(x);        }        else if(s[0]=='R')        {            x=st.top();            st.pop();            update(x,n,-1);        }        else        {            scanf("%d",&x);            x++;            int k=sum(x,n);            int pre=find_kth(k,n,logn);            int next=find_kth(k+1,n,logn);            if(pre==x) puts("0");            else printf("%d\n",next-pre-1);        }    }    return 0;}

解法二:线段树区间合并

这个和hotel那题差不多,都是区间合并,代码差不多。

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<map>#include<queue>#include<set>#include<stack>#include<cmath>#include<bitset>#define mem(a,b) memset(a,b,sizeof(a))#define lson i<<1,l,mid#define rson i<<1|1,mid+1,r#define INF 10000010#define maxn 200010using namespace std;typedef long long ll;typedef unsigned long long ull;int sum[maxn],lsum[maxn],rsum[maxn];void pushup(int i,int v){    int l=i<<1,r=i<<1|1;    lsum[i]=lsum[l];    if(lsum[l]==v-(v>>1))//若左子树全空        lsum[i]+=lsum[r];//则加上右子树的左区间    rsum[i]=rsum[r];    if(rsum[r]==v>>1)        rsum[i]+=rsum[l];    sum[i]=max(max(sum[l],sum[r]),rsum[l]+lsum[r]);}void update(int i,int l,int r,int x,int v){    if(l==r)    {        sum[i]=lsum[i]=rsum[i]=v;        return ;    }    int mid=(l+r)>>1;    if(x<=mid) update(lson,x,v);    else update(rson,x,v);    pushup(i,r-l+1);}int query(int i,int l,int r,int v){    if(l==r||sum[i]==0||sum[i]==r-l+1) return sum[i];    int mid=(l+r)>>1;    if(v<=mid)    {        if(v>=mid-rsum[i<<1]+1)            return query(lson,v)+query(rson,mid+1);        else return query(lson,v);    }    else    {        if(v<=mid+lsum[i<<1|1])            return query(rson,v)+query(lson,mid);        else return query(rson,v);    }}void build(int i,int l,int r){    sum[i]=lsum[i]=rsum[i]=r-l+1;    if(l==r) return ;    int mid=(l+r)>>1;    build(lson);    build(rson);}int main(){    //freopen("test.txt","r",stdin);    int n,m;    while(~scanf("%d%d",&n,&m))    {        build(1,1,n);        stack<int>st;        while(m--)        {            int x;            char s[2];            scanf("%s",s);            if(s[0]=='D')            {                scanf("%d",&x);                st.push(x);                update(1,1,n,x,0);            }            else if(s[0]=='R')            {                x=st.top();st.pop();                update(1,1,n,x,1);            }            else            {                scanf("%d",&x);                printf("%d\n",query(1,1,n,x));            }        }    }    return 0;}

第八道:

HDU 2871  区间合并 

这题比较晕……靠……刚开始直接输入scanf("%s%d",s,%l);然后一直T,检查了好久都没发现哪里有超时的可能。然后不用文件输入,手动输入的时候才发现Reset后面不用输数字了,尼玛……晕……

与hotel那题的函数是一样的,只不过处理不一样。

不过这里因为是一堆一堆处理的,所以比较麻烦,但是线段树的函数还是不变的。

因为要处理询问一段一段,所以可以把其放进向量vector里记录,加入和删除比较方便。

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<vector>#define lson i<<1,l,mid#define rson i<<1|1,mid+1,r#define maxn 200010using namespace std;typedef long long ll;typedef unsigned long long ull;int sum[maxn],lsum[maxn],rsum[maxn],lazy[maxn];void pushdown(int i,int v){    if(lazy[i]!=-1)    {        int l=i<<1,r=i<<1|1;        lazy[l]=lazy[r]=lazy[i];        sum[l]=lsum[l]=rsum[l]=(lazy[i]?0:(v-(v>>1)));//lazy为1表清空房间        sum[r]=lsum[r]=rsum[r]=(lazy[i]?0:(v>>1));//右孩子值        lazy[i]=-1;    }}void pushup(int i,int v){    int l=i<<1,r=i<<1|1;    lsum[i]=lsum[l];    if(lsum[l]==v-(v>>1))//若左子树全空        lsum[i]+=lsum[r];//则加上右子树的左区间    rsum[i]=rsum[r];    if(rsum[r]==v>>1)        rsum[i]+=rsum[l];    sum[i]=max(max(sum[l],sum[r]),rsum[l]+lsum[r]);}void update(int i,int l,int r,int L,int R,int v){    if(L<=l&&r<=R)    {        sum[i]=lsum[i]=rsum[i]=(v?0:r-l+1);        lazy[i]=v;        return ;    }    pushdown(i,r-l+1);    int mid=(l+r)>>1;    if(L<=mid) update(lson,L,R,v);    if(mid<R) update(rson,L,R,v);    pushup(i,r-l+1);}int query(int i,int l,int r,int v){    if(sum[i]<v) return 0;    if(l==r) return l;    pushdown(i,r-l+1);    int mid=(l+r)>>1;    if(sum[i<<1]>=v) return query(lson,v);    else if(rsum[i<<1]+lsum[i<<1|1]>=v)        return mid-rsum[i<<1]+1;    return query(rson,v);}void build(int i,int l,int r){    sum[i]=lsum[i]=rsum[i]=r-l+1;    lazy[i]=-1;    if(l==r) return ;    int mid=(l+r)>>1;    build(lson);    build(rson);}struct block{    int Begin,End;}y;bool cmp(const block &a,const block &b){    return a.Begin<b.Begin;}vector<block>v;vector<block>::iterator it;int main(){    //freopen("test.txt","r",stdin);    int n,m;    while(~scanf("%d%d",&n,&m))    {        build(1,1,n);        v.clear();        while(m--)        {            int l;            char s[7];            scanf("%s",&s);            if(s[0]=='N')            {                scanf("%d",&l);                if(sum[1]<l) { puts("Reject New");continue; }                int k=query(1,1,n,l);                printf("New at %d\n",k);                update(1,1,n,k,k+l-1,1);                y.Begin=k,y.End=k+l-1;                it=upper_bound(v.begin(),v.end(),y,cmp);                v.insert(it,y);            }            else if(s[0]=='F')            {                scanf("%d",&l);                y.Begin=l,y.End=l;                it=upper_bound(v.begin(),v.end(),y,cmp);                int ii=it-v.begin()-1;                if(ii==-1||v[ii].End<l)                    puts("Reject Free");                else                {                    printf("Free from %d to %d\n",v[ii].Begin,v[ii].End);                    update(1,1,n,v[ii].Begin,v[ii].End,0);                    v.erase(v.begin()+ii);                }            }            else if(s[0]=='R')            {                //build(1,1,n);会超时                update(1,1,n,1,n,0);                v.clear();                puts("Reset Now");            }            else            {                scanf("%d",&l);                if(l>v.size()) puts("Reject Get");                else printf("Get at %d\n",v[l-1].Begin);            }        }        puts("");    }    return 0;}

第九道:

HDU 1542 开始利用线段树求解矩形面积的并、交、以及周长.

刚开始接触这类型的时候,真的不知道该如何下手。没想到线段树还能拿来求解矩形面积的并、交和周长,感觉挺神的,后面就看了这方面的博客,看到一个挺好的解释和代码,研究了一天半才全部搞明白……

先看这个博客前面线段树中结点域的解释,然后边看图边看代码就理解了。

昨天看明白之后,还以为全部理解了,然后今天敲的时候,没得答案,然后把过程都输出了,一个一个对比,然后才发现建树的时候错了。我们一般建树都是build(i<<1,l,mid);build(1<<1|1,mid+1,r);而这个线段树求解矩形的并不这样建的,有点区别。因为这样建树的话,叶子结点l和r相同,就是一个点,而不是线段了,所以要让叶子结点是线段,然后又不能在更新线段的时候变成更新一个点,所以得这样建树:build(1<<1,l,mid);build(1<<1|1,mid,r);建的这个右子树不一样。

学习参考博客:http://www.cnblogs.com/ka200812/archive/2011/11/13/2247064.html

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<map>#include<queue>#include<set>#include<cmath>#include<bitset>#define mem(a,b) memset(a,b,sizeof(a))#define lson i<<1,l,mid#define rson i<<1|1,mid+1,r#define INF 510010#define maxn 400010using namespace std;typedef long long ll;typedef unsigned long long ull;double y[210];struct line{    double x,y1,y2;    int flag;}a[210];bool cmp(line a,line b){    return a.x<b.x;}double len[1000];int lazy[1000];void pushUp(int i,int l,int r){    if(lazy[i]) len[i]=y[r-1]-y[l-1];    else if(l+1==r) len[i]=0;//叶子结点    else len[i]=len[i<<1]+len[i<<1|1];}void update(int i,int l,int r,int L,int R,int c){    if(r<=L||l>=R) return ;    if(L<=l&&r<=R)    {        lazy[i]+=c;        pushUp(i,l,r);        return;    }    int mid=(l+r)>>1;    update(lson,L,R,c);    update(i<<1|1,mid,r,L,R,c);//建的树不一样    pushUp(i,l,r);}int main(){    //freopen("test.txt","r",stdin);    int n,ii=1;    while(scanf("%d",&n)&&n)    {        int i;        double x1,y1,x2,y2,ans=0;        mem(len,0);mem(lazy,0);        for(i=0;i<n;i++)        {            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);            y[i<<1]=y1,a[i<<1].x=x1,a[i<<1].y1=y1,a[i<<1].y2=y2,a[i<<1].flag=1;            y[i<<1|1]=y2,a[i<<1|1].x=x2,a[i<<1|1].y1=y1,a[i<<1|1].y2=y2,a[i<<1|1].flag=-1;        }        sort(y,y+n*2); //因为是浮点数,而且数又大,不好建树        int nn=unique(y,y+n*2)-y;//所以离散化了再建树        sort(a,a+n*2,cmp);        printf("Test case #%d\n",ii++);        for(i=0;i<n*2-1;i++)        {            int l=lower_bound(y,y+nn,a[i].y1)-y+1;            int r=lower_bound(y,y+nn,a[i].y2)-y+1;            update(1,1,nn,l,r,a[i].flag);            ans+=len[1]*(a[i+1].x-a[i].x);        }        printf("Total explored area: %.2f\n\n",ans);    }    return 0;}

第十道:

HDU 1255 求解矩形并的面积

这题和上一题处理的代码都一样,我还没变呢就过了……算的时候当然是用覆盖两次以上的算啦!在上一题中把线段树节点里面的len域改成覆盖一次和覆盖两次的就行了,覆盖两次以上的肯定有并的了。比较难的处理在pushUp那个函数,只要理解了上一题,这一题应该没有那么难理解了。

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<map>#include<queue>#include<set>#include<cmath>#include<bitset>#define mem(a,b) memset(a,b,sizeof(a))#define lson i<<1,l,mid#define rson i<<1|1,mid+1,r#define INF 510010#define maxn 800010using namespace std;typedef long long ll;typedef unsigned long long ull;double y[2010];struct line{    double x,y1,y2;    int flag;} a[2010];bool cmp(line a,line b){    return a.x<b.x;}double one_len[maxn],more_len[maxn];//覆盖一次或多次int lazy[maxn];void pushUp(int i,int l,int r){    if(lazy[i]>1)        one_len[i]=more_len[i]=y[r-1]-y[l-1];//覆盖两次以上的话,一次和多次的长度是一样的    else if(lazy[i]==1)    {        one_len[i]=y[r-1]-y[l-1];//一次覆盖        if(l+1==r) more_len[i]=0;//叶子结点多次覆盖定为0        else more_len[i]=one_len[i<<1]+one_len[i<<1|1];//因为当前已经有覆盖的话,要是下面的也有覆盖一次,那肯定覆盖两次以上啦,画个图就理解了!    }    else    {        if(l+1==r)            more_len[i]=one_len[i]=0;//一次覆盖都没有的时候在叶子都为0        else        {            more_len[i]=more_len[i<<1]+more_len[i<<1|1];            one_len[i]=one_len[i<<1]+one_len[i<<1|1];//往上传        }    }}void update(int i,int l,int r,int L,int R,int c){    if(r<=L||l>=R) return ;    if(L<=l&&r<=R)    {        lazy[i]+=c;        pushUp(i,l,r);        return;    }    int mid=(l+r)>>1;    update(lson,L,R,c);    update(i<<1|1,mid,r,L,R,c);//建的树不一样    pushUp(i,l,r);}int main(){    //freopen("test.txt","r",stdin);    int t;    cin>>t;    while(t--)    {        int n;        scanf("%d",&n);        int i;        double x1,y1,x2,y2,ans=0;        mem(one_len,0);mem(more_len,0);        mem(lazy,0);        for(i=0; i<n; i++)        {            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);            y[i<<1]=y1,a[i<<1].x=x1,a[i<<1].y1=y1,a[i<<1].y2=y2,a[i<<1].flag=1;            y[i<<1|1]=y2,a[i<<1|1].x=x2,a[i<<1|1].y1=y1,a[i<<1|1].y2=y2,a[i<<1|1].flag=-1;        }        sort(y,y+n*2); //因为是浮点数,而且数又大,不好建树        int nn=unique(y,y+n*2)-y;//所以离散化了再建树        sort(a,a+n*2,cmp);        for(i=0; i<n*2-1; i++)        {            int l=lower_bound(y,y+nn,a[i].y1)-y+1;            int r=lower_bound(y,y+nn,a[i].y2)-y+1;            update(1,1,nn,l,r,a[i].flag);            ans+=more_len[1]*(a[i+1].x-a[i].x);//用多次覆盖的计算        }        printf("%.2f\n",ans);    }    return 0;}


第十一道:

HDU 1828 线段树求矩形并的周长

其实就是用了上面第九道题的模板就A了,哈哈,太爽了。

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<map>#include<queue>#include<set>#include<cmath>#include<bitset>#define mem(a,b) memset(a,b,sizeof(a))#define lson i<<1,l,mid#define rson i<<1|1,mid+1,r#define INF 510010#define maxn 400010using namespace std;typedef long long ll;typedef unsigned long long ull;struct line{    int x,y1,y2;    int flag;}a[maxn/7],b[maxn/7];bool cmp(line a,line b){    return a.x<b.x;}int len[maxn],lazy[maxn],y[maxn/7],x[maxn/7];void pushUp(int i,int l,int r,int flag){    if(lazy[i])    {        if(flag) len[i]=y[r-1]-y[l-1];        else len[i]=x[r-1]-x[l-1];    }    else if(l+1==r) len[i]=0;//叶子结点    else len[i]=len[i<<1]+len[i<<1|1];}void update(int i,int l,int r,int L,int R,int c,int flag){    if(r<=L||l>=R) return ;    if(L<=l&&r<=R)    {        lazy[i]+=c;        pushUp(i,l,r,flag);        return;    }    int mid=(l+r)>>1;    update(lson,L,R,c,flag);    update(i<<1|1,mid,r,L,R,c,flag);//建的树不一样    pushUp(i,l,r,flag);}int main(){    //freopen("test.txt","r",stdin);    int n,ii=1;    while(~scanf("%d",&n))    {        int i,x1,y1,x2,y2,ans=0;        mem(len,0);mem(lazy,0);        for(i=0;i<n;i++)        {            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);            int l=i<<1,r=i<<1|1;            y[l]=y1,a[l].x=x1,a[l].y1=y1,a[l].y2=y2,a[l].flag=1;            y[r]=y2,a[r].x=x2,a[r].y1=y1,a[r].y2=y2,a[r].flag=-1;            x[l]=x1,b[l].x=y1,b[l].y1=x1,b[l].y2=x2,b[l].flag=1;            x[r]=x2,b[r].x=y2,b[r].y1=x1,b[r].y2=x2,b[r].flag=-1;        }        sort(y,y+n*2); //因为是浮点数,而且数又大,不好建树        int nn=unique(y,y+n*2)-y;//所以离散化了再建树        sort(a,a+n*2,cmp);        int parent=0;        for(i=0;i<n*2;i++)        {            int l=lower_bound(y,y+nn,a[i].y1)-y+1;            int r=lower_bound(y,y+nn,a[i].y2)-y+1;            update(1,1,nn,l,r,a[i].flag,1);            ans+=abs(len[1]-parent);            parent=len[1];        }        mem(len,0);mem(lazy,0);        sort(x,x+n*2); //因为是浮点数,而且数又大,不好建树        nn=unique(x,x+n*2)-x;//所以离散化了再建树        sort(b,b+n*2,cmp);        parent=0;        for(i=0;i<n*2;i++)        {            int l=lower_bound(x,x+nn,b[i].y1)-x+1;            int r=lower_bound(x,x+nn,b[i].y2)-x+1;            update(1,1,nn,l,r,b[i].flag,0);            ans+=abs(len[1]-parent);            parent=len[1];        }        printf("%d\n",ans);    }    return 0;}

第十二道:

POJ 2828 逆序查找空位插入法

思路:线段树单点更新查找空位插入。

不理解的可以看这个网址的解释:http://www.cnblogs.com/CheeseZH/archive/2012/04/29/2476134.html

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<map>#include<queue>#include<set>#include<cmath>#include<bitset>#define mem(a,b) memset(a,b,sizeof(a))#define lson i<<1,l,mid#define rson i<<1|1,mid+1,r#define INF 510010#define maxn 400010using namespace std;typedef long long ll;typedef unsigned long long ull;int sum[maxn*4],p[maxn],val[maxn],a[maxn],id;void build(int i,int l,int r){    sum[i]=r-l+1;    if(l==r) return ;    int mid=(l+r)>>1;    build(lson);build(rson);}void update(int i,int l,int r,int v){    sum[i]--;    if(l==r) {id=l;return;}    int mid=(l+r)>>1;    if(v>sum[i<<1])    {        v-=sum[i<<1];        update(rson,v);    }    else update(lson,v);}int main(){    int n,i;    while(~scanf("%d",&n))    {        build(1,1,n);        for(i=1;i<=n;i++)            scanf("%d%d",p+i,val+i);        for(i=n;i>0;i--)        {            update(1,1,n,p[i]+1);            a[id]=val[i];        }        printf("%d",a[1]);        for(i=2;i<=n;i++)            printf(" %d",a[i]);        puts("");    }    return 0;}

第十三道:

POJ 2155  二维线段树

思路:二维线段树就是每个节点套一棵线段树的树。

刚开始因为题目是求A[I,J],然后在y查询那直接ans^=Map[i][j]的时候没看懂,后面自己把图画出来了才理解。

因为只有0和1,所以可以用异或来搞,而不需要每次都需要修改。

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<map>#include<queue>#include<set>#include<cmath>#include<bitset>#define mem(a,b) memset(a,b,sizeof(a))#define lson i<<1,l,mid#define rson i<<1|1,mid+1,r#define llson j<<1,l,mid#define rrson j<<1|1,mid+1,r#define INF 510010#define maxn 4010using namespace std;typedef long long ll;typedef unsigned long long ull;bool Map[maxn][maxn];int n,q,t,ans;void update_y(int i,int j,int l,int r,int y1,int y2){    if(l==y1&&r==y2) {Map[i][j]^=1;return ;}    int mid=(l+r)>>1;    if(y2<=mid) update_y(i,llson,y1,y2);    else if(y1>mid) update_y(i,rrson,y1,y2);    else    {        update_y(i,llson,y1,mid);        update_y(i,rrson,mid+1,y2);    }}void update_x(int i,int l,int r,int x1,int x2,int y1,int y2){    if(l==x1&&r==x2)    {        update_y(i,1,1,n,y1,y2);        return ;    }    int mid=(l+r)>>1;    if(x2<=mid) update_x(lson,x1,x2,y1,y2);    else if(x1>mid) update_x(rson,x1,x2,y1,y2);    else    {        update_x(lson,x1,mid,y1,y2);        update_x(rson,mid+1,x2,y1,y2);    }}void query_y(int i,int j,int l,int r,int y){    ans^=Map[i][j];    if(l==r) return ;    int mid=(l+r)>>1;    if(y<=mid) query_y(i,llson,y);    else query_y(i,rrson,y);}void query_x(int i,int l,int r,int x,int y){    query_y(i,1,1,n,y);    if(l==r) return ;    int mid=(l+r)>>1;    if(x<=mid) query_x(lson,x,y);    else query_x(rson,x,y);}int main(){    //freopen("test.txt","r",stdin);    scanf("%d",&t);    while(t--)    {        scanf("%d%d",&n,&q);        mem(Map,0);        while(q--)        {            char s[2];            scanf("%s",s);            ans=0;            if(s[0]=='C')            {                int x1,x2,y1,y2;                scanf("%d%d%d%d",&x1,&y1,&x2,&y2);                update_x(1,1,n,x1,x2,y1,y2);            }            else            {                int x,y;                scanf("%d%d",&x,&y);                query_x(1,1,n,x,y);                printf("%d\n",ans);            }        }        if(t) puts("");    }    return 0;}

第十四道:

ZOJ 2859 二维线段树

思路:自己写的第二发二维线段树1A,哈哈,看来对二维的push操作比较了解了;但是还没遇到在两个线段树中同时进行push操作的,其实这题我是想在x维和y维同时进行push操作的,但是想了好久不会,然后看到这题又给出10秒,然后想想在x维线段直接单点查询肯定也过了,然后就只有pushup操作,没有lazy延时pushdown操作。不过也A了,想找题即在二维同时进行pushup和pushdown操作的。

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<map>#include<queue>#include<set>#include<cmath>#include<bitset>#define mem(a,b) memset(a,b,sizeof(a))#define lson i<<1,l,mid#define rson i<<1|1,mid+1,r#define llson j<<1,l,mid#define rrson j<<1|1,mid+1,r#define INF 0x7fffffff#define maxn 1010using namespace std;typedef long long ll;typedef unsigned long long ull;int Map[maxn][maxn],Min[maxn][maxn];int n,q,t,ans;void pushup(int i,int j){    Min[i][j]=min(Min[i][j<<1],Min[i][j<<1|1]);}void build_y(int i,int u,int j,int l,int r){    if(l==r)    {        Min[i][j]=Map[u][l];        return ;    }    int mid=(l+r)>>1;    build_y(i,u,llson);build_y(i,u,rrson);    pushup(i,j);}void build_x(int i,int l,int r){    if(l==r)    {        build_y(i,l,1,1,n);        return ;    }    int mid=(l+r)>>1;    build_x(lson);build_x(rson);}int query_y(int i,int j,int l,int r,int y1,int y2){    if(l==y1&&y2==r) return Min[i][j];    int mid=(l+r)>>1;    if(y2<=mid) return query_y(i,llson,y1,y2);    else if(y1>mid) return query_y(i,rrson,y1,y2);    else return min(query_y(i,llson,y1,mid),query_y(i,rrson,mid+1,y2));}void query_x(int i,int l,int r,int x1,int x2,int y1,int y2){    if(l==r)    {        ans=min(ans,query_y(i,1,1,n,y1,y2));        return ;    }    int mid=(l+r)>>1;    if(x1<=mid) query_x(lson,x1,x2,y1,y2);    if(x2>mid) query_x(rson,x1,x2,y1,y2);}int main(){    freopen("test.txt","r",stdin);    scanf("%d",&t);    while(t--)    {        scanf("%d",&n);        for(int i=1;i<=n;i++)            for(int j=1;j<=n;j++)                scanf("%d",&Map[i][j]);        build_x(1,1,n);        scanf("%d",&q);        while(q--)        {            int x1,y1,x2,y2;            ans=INF;            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);            query_x(1,1,n,x1,x2,y1,y2);            printf("%d\n",ans);        }    }    return 0;}

HDU 4614

思路:把区间空的地方记录下来求和,然后1的时候求1到a-1的0个数为cnt,查询cnt+1的位置就是第一个位置,查询cnt+b的位置就是最后一个位置;2的时候直接查询,然后再更新。

#pragma comment(linker, "/STACK:1024000000,1024000000")#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<map>#include<queue>#include<set>#include<cmath>#include<bitset>#include<time.h>#define mem(a,b) memset(a,b,sizeof(a))#define lson i<<1,l,mid#define rson i<<1|1,mid+1,r#define llson j<<1,l,mid#define rrson j<<1|1,mid+1,r#define INF 0x7ffffffftypedef long long ll;typedef unsigned long long ull;using namespace std;#define maxn 1000005int sum[maxn],lazy[maxn];void build(int i,int l,int r){    sum[i]=r-l+1,lazy[i]=0;    if(l==r) return ;    int mid=(l+r)>>1;    build(lson),build(rson);}void pushDown(int i,int l,int r){    if(lazy[i]&&l!=r)    {        if(lazy[i]==-1) sum[i<<1]=sum[i<<1|1]=0;        else        {            sum[i<<1]=((r-l)>>1)+1;            sum[i<<1|1]=(r-l+1)>>1;        }        lazy[i<<1]=lazy[i<<1|1]=lazy[i];        lazy[i]=0;    }}void pushUp(int i){    sum[i]=sum[i<<1]+sum[i<<1|1];}void update(int i,int l,int r,int L,int R,int val){    if(L<=l&&r<=R)    {        if(val==-1) sum[i]=0;        else sum[i]=r-l+1;        lazy[i]=val;        return ;    }    pushDown(i,l,r);    int mid=(l+r)>>1;    if(R<=mid) update(lson,L,R,val);    else if(L>mid) update(rson,L,R,val);    else    {        update(lson,L,mid,val);        update(rson,mid+1,R,val);    }    pushUp(i);}int query(int i,int l,int r,int L,int R){    if(L<=l&&r<=R) return sum[i];    int mid=(l+r)>>1,ans=0;    pushDown(i,l,r);    if(R<=mid) ans=query(lson,L,R);    else if(L>mid) ans=query(rson,L,R);    else ans=query(lson,L,mid)+query(rson,mid+1,R);    pushUp(i);    return ans;}int findPos(int i,int l,int r,int L,int R,int val){    if(l==r) return l;    int mid=(l+r)>>1,pos=0;    pushDown(i,l,r);    if(R<=mid) pos=findPos(lson,L,R,val);    else if(L>mid) pos=findPos(rson,L,R,val);    else    {        if(sum[i<<1]>=val) pos=findPos(lson,L,mid,val);        else pos=findPos(rson,mid+1,R,val-sum[i<<1]);    }    pushUp(i);    return pos;}int main(){    //freopen("1.txt","r",stdin);    int t;    scanf("%d",&t);    while(t--)    {        int n,m,q,a,b;        scanf("%d%d",&n,&m);        build(1,1,n);        while(m--)        {            scanf("%d%d%d",&q,&a,&b);            if(q==1)            {                a++;                int tmp=query(1,1,n,a,n);                if(!tmp)                {                    puts("Can not put any one.");                    continue;                }                if(tmp<b) b=tmp;                int sum1=0;                if(a-1>=1) sum1=query(1,1,n,1,a-1);                int pos1=findPos(1,1,n,1,n,sum1+1);                int pos2=findPos(1,1,n,1,n,sum1+b);                update(1,1,n,pos1,pos2,-1);                printf("%d %d\n",pos1-1,pos2-1);            }            else            {                a++,b++;                int sum1=query(1,1,n,a,b);                update(1,1,n,a,b,1);                printf("%d\n",b-a+1-sum1);            }        }        puts("");    }    return 0;}




0 0