线段树练习题

第一题:
递交超链接((http://blog.csdn.net/qq_36038511/article/details/54561938))

第二题:
市长的海报(来源:POJ2528)
(posters.cpp)
【题意】
n(n<=10000)个人依次贴海报,给出每张海报所贴的范围li,ri(1<=li<=ri<=10000000)。
求出最后还能看见多少张海报。(注意:没多组数据)

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
struct ls
{
    int x,z,p;
}a[21000],b[21000];//离散化专用
int s[21000];
struct trnode
{
    int l,r,c,lc,rc;
}tr[81000];int len;
bool v[11000];
int lscmp(const void *xx,const void *yy)//自定义快排
{
    ls n1=*(ls *)xx;
    ls n2=*(ls *)yy;
    return n1.x-n2.x;
}
void bt(int l,int r)
{
    len++;int now=len;
    tr[now].l=l;tr[now].r=r;tr[now].lc=tr[now].rc=-1; tr[now].c=0;
    if(l<r)
    {
        int mid=(l+r)/2;
        tr[now].lc=len+1;bt(l,mid);
        tr[now].rc=len+1;bt(mid+1,r);
    }
}
void change(int now,int l,int r,int k)
{
    if(tr[now].c==k) return;
    //如果当前这部分已经更新过了,就没有必要更新了(或者说已经贴了这张海报了)
    if(tr[now].l==l && tr[now].r==r)
    {
        tr[now].c=k;
        return;
    }
    int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)/2;
    if(tr[now].c>=0)//lazy更新
    {
        tr[lc].c=tr[now].c;
        tr[rc].c=tr[now].c;
    }
    if(r<=mid) change(lc,l,r,k);
    else if(mid+1<=l) change(rc,l,r,k);
    else
    {
        change(lc,l,mid,k);
        change(rc,mid+1,r,k);
    }
    if(tr[lc].c==tr[rc].c) tr[now].c=tr[lc].c;
    else tr[now].c=-1;
}
void query(int now,int l,int r)//查找贴过的海报
{
    if(tr[now].c>=0){v[tr[now].c]=true;return;}
    int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)/2;
    if(r<=mid) query(lc,l,r);
    else if(mid+1<=l) query(rc,l,r);
    else
    {
        query(lc,l,mid);
        query(rc,mid+1,r);
    }
}
int main()
{
    freopen("posters.in","r",stdin);
    freopen("posters.out","w",stdout);
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&a[2*i-1].x,&a[2*i].x);
        a[2*i-1].p=2*i-1;
        a[2*i].p=2*i;
    }
    for(int i=1;i<=2*n;i++){b[i].x=a[i].x,b[i].p=a[i].p;}
    qsort(b+1,2*n,sizeof(ls),lscmp);
    b[1].z=1;
    for(int i=2;i<=2*n;i++)
    {
        if(b[i].x==b[i-1].x) b[i].z=b[i-1].z;
        else if(b[i].x-b[i-1].x==1) b[i].z=b[i-1].z+1;
        else if(b[i].x-b[i-1].x>1) b[i].z=b[i-1].z+2;
    }
    for(int i=1;i<=2*n;i++) s[b[i].p]=b[i].z;
    //以上为离散化
    len=0;bt(1,b[2*n].z);
    for(int i=1;i<=n;i++)change(1,s[2*i-1],s[2*i],i);
    memset(v,false,sizeof(v));
    query(1,1,b[2*n].z);
    int ans=0;for(int i=1;i<=n;i++)if(v[i]==true)ans++;//累加能看到的海报
    printf("%d\n",ans);
    return 0;
}

推荐一个好的算法:
如果更新次数太多的话,可以考虑从后往前更新
即如果更新时发现之前已经有一张海报完全覆盖他了
就可以不用更新(因为没有必要)
这样的算法时间会减少很多

第三题:
A Line painting(来源:ural1019)
(bw.cpp)
【题目描述】
先是在数轴区间 0 到10^9 (10的9次方)之间画上了白色。然后,这个区间的某一些部分又画上了黑色。然后某一些部分又画上白色,等等。请你找出经历N(1 <= N <= 5000)次重着色后,最长的白色区间。
【输入格式】
首行位N,以下N行位重着色的信息,每一行格式如下:
ai bi ci 这里 ai ,bi 都是整数, ci 为字符’b’ 或’w’,用空格隔开。
这三个参数描述:从ai到bi,着颜色ci, (‘w’表示白,’b’表示黑),可以认为0 < ai <= bi < 10**9.
【输出格式】
输出x,y (x < y),之间用空格隔开,表示最长的白色区间。假如有多个答案,输出x最小的那个

代码:

#include<cstdio>
#include<cstdlib>
#include<cstring>

int len=0;
struct node{int l,r,lc,rc,c;bool update;};
struct paint{int a,b;char c;};
struct ls{int o,n;};
node t[20020];
paint p[10010];
ls pp[20020];
int ll=0;
int n;

void qsort(int l,int r)
{
    int i,j;
    int m;
    ls t;
    i=l;j=r;
    m=pp[(i+j)>>1].o;
    while (i<=j)
    {
        while (pp[i].o<m) i++;
        while (pp[j].o>m) j--;
        if (i<=j)
        {
            t=pp[i];pp[i]=pp[j];pp[j]=t;
            i++;j--;
        }
    }
    if (l<j) qsort(l,j);
    if (i<r) qsort(i,r);
}

int wz(int x)
{
    int l=1,r=ll,m;
    while (1)
    {
        m=(l+r)>>1;
        if (pp[m].o==x) break;
        if (pp[m].o>x) r=m-1;
        else l=m+1;
    }
    return pp[m].n;

}

int build(int l,int r)
{
    len++;
    int x=len;
    t[x].l=l;t[x].r=r;t[x].c=0;t[x].update=false;
    if (l+1!=r)
    {
        int mid;
        mid=(l+r)>>1;
        t[x].lc=build(l,mid);
        t[x].rc=build(mid,r);
    }
    return x;
}

void update(int x)
{
    t[x].update=false;
    t[t[x].lc].update=true;
    t[t[x].lc].c=t[x].c;
    t[t[x].rc].update=true;
    t[t[x].rc].c=t[x].c;
}

void insert(int x,int l,int r,int c)
{
    if (t[x].l==l && t[x].r==r)
    {
        t[x].c=c;
        t[x].update=true;
        return;
    }
    int mid,lc,rc;
    mid=(t[x].l+t[x].r)>>1;
    lc=t[x].lc;rc=t[x].rc;
    if (t[x].update) update(x);
    if (r<=mid) insert(lc,l,r,c);
    else if (mid<=l) insert(rc,l,r,c);
    else
    {
        insert(lc,l,mid,c);
        insert(rc,mid,r,c);}
    if (t[lc].c==t[rc].c) t[x].c=t[lc].c;
    else t[x].c=-1;
}

int getcount(int x,int l,int r)
{
    if (t[x].l==l && t[x].r==r) 
    {
        return t[x].c;
    }
    int mid,lc,rc;
    mid=(t[x].l+t[x].r)>>1;
    lc=t[x].lc;rc=t[x].rc;
    if (t[x].update) update(x);
    if (r<=mid) return getcount(lc,l,r);
    if (mid<=l) return getcount(rc,l,r);
}

int main()
{
    freopen("bw.in","r",stdin);
    freopen("bw.out","w",stdout);
    scanf("%d",&n);
    pp[1].o=0;pp[2].o=1000000000;
    ll=2;
    for (int i=1;i<=n;i++)
    {
        scanf("%d %d %c",&p[i].a,&p[i].b,&p[i].c);
        pp[++ll].o=p[i].a;pp[++ll].o=p[i].b;
        getchar();
    }
    qsort(1,ll);
    pp[1].n=1;
    for (int i=2;i<=ll;i++)
    {
        if (pp[i].o==pp[i-1].o) pp[i].n=pp[i-1].n;
        else pp[i].n=i;
    }
    build(1,ll);
    for (int i=1;i<=n;i++)
    {
        if (p[i].a==p[i].b) continue;
        //printf("%d %d\n",wz(p[i].a),wz(p[i].b));
        insert(1,wz(p[i].a),wz(p[i].b),(p[i].c=='b'));
        //printf("ok\n");
    }
    //printf("XX\n");
    int now=0,ans=0,ansl,ansr,l=0,r=1;
    for (int i=1;i<ll;i++)
    {
        if (getcount(1,i,i+1)==0) {r=pp[i+1].o;now=r-l;}
        else {l=pp[i+1].o;now=0;}
        if (now>ans) {ans=now;ansl=l;ansr=r;};
    }
    printf("%d %d",ansl,ansr);
}

儿童节快乐
(happy.cpp)
【描述】
儿童节即将到来。在这一天,小朋友们会得到好多糖果。在MAX城市,人民发明了一种糖果自动管理系统(ACM系统),该系统能管理N堆糖果。系统只能执行两种操作。
  (1)I a b c(1≤a≤b≤N,0 < c≤100),ACM将在堆a至堆b之间(包含a和b)每堆糖果加c个。
  (2)C a b(1≤a≤b≤N),ACM将会选择a到b堆之间糖果数最多的那堆糖果送给一个小朋友。如果有两堆或两堆以上糖果数为最大值,选择那么编号小的那堆。
  给出一系列的操作,对于每个C操作,输出堆的糖果数。

【输入】
  有多组测试数据。
  每组测试数据的第一行为两个整数N,M(0< N,M≤10^5),N表示糖果堆的数目,M表示操作的次数。
  下来M行,每行为一个操作。
  输入当N=0并且M=0时结束,并且不做任何处理。
  初始时,所有堆糖果数目为0。
【输出】
  对于每个C操作,输出小朋友能得到的糖果的数目。
  

某师兄的代码:

#include<cstdio>
#include<cstring>
const int N=100005;
int n,m;
struct qq
{
    int l,r;
    int s1,s2;
    int z,z1;//最大的值 最大是哪一个编号
    int lazy; 
}s[N*2];
int num;
void bt (int l,int r)
{
    num++;
    int a=num;
    s[a].l=l;s[a].r=r;
    s[a].s1=s[a].s2=-1;
    s[a].z=0;
    s[a].z1=l;
    s[a].lazy=0;
    if (l==r) return ;
    int mid=(l+r)/2;
    s[a].s1=num+1;bt(l,mid);
    s[a].s2=num+1;bt(mid+1,r);
    return ;
}
void add (int now,int l,int r,int c)
{
    //printf("%d %d z:%d lazy:%d\n",s[now].l,s[now].r,s[now].z,s[now].lazy);
    if (s[now].l==s[now].r)
    {
    //  printf("YES:%d %d\n",l,r);
        s[now].z+=c;
        return ;
    }
    if (s[now].l==l&&s[now].r==r)
    {
        s[now].lazy+=c;
        s[now].z+=c;
        return ;
    }
    int s1=s[now].s1,s2=s[now].s2;
    s[s1].lazy+=s[now].lazy;
    s[s1].z+=s[now].lazy;
    s[s2].lazy+=s[now].lazy;
    s[s2].z+=s[now].lazy;
    s[now].lazy=0;
    int mid=(s[now].l+s[now].r)/2;
    if (r<=mid) add(s1,l,r,c);
    else if (l>mid) add(s2,l,r,c);
    else
    {
        add(s1,l,mid,c);
        add(s2,mid+1,r,c);
    }
    if (s[s1].z>=s[s2].z)
    {
        s[now].z=s[s1].z;
        s[now].z1=s[s1].z1;
    }
    else
    {
        s[now].z=s[s2].z;
        s[now].z1=s[s2].z1;
    }
    return ;
}
int z,z1;
void find (int now,int l,int r)
{
    if (s[now].l==l&&s[now].r==r)
    {
        z=s[now].z;
        z1=s[now].z1;
        return ;
    }
    int s1=s[now].s1,s2=s[now].s2;
    s[s1].lazy+=s[now].lazy;
    s[s1].z+=s[now].lazy;
    s[s2].lazy+=s[now].lazy;
    s[s2].z+=s[now].lazy;
    s[now].lazy=0;
    int mid=(s[now].l+s[now].r)/2;
    if (r<=mid) find (s1,l,r);
    else if (l>mid) find (s2,l,r);
    else
    {
        find(s1,l,mid);
        int a=z,a1=z1;
        find(s2,mid+1,r);
        if (a>=z)
        {
            z=a;
            z1=a1;
        }
    }
    return ;
}
int main()
{
    freopen("happy.in","r",stdin);
    freopen("happy.out","w",stdout);
    while (scanf("%d%d",&n,&m))
    {
    if (n==0&&m==0) break;
    num=0;bt(1,n);
    for (int u=1;u<=m;u++)
    {
    //for (int u=1;u<=num;u++) printf("%d %d %d %d z:%d z1:%d lazy:%d\n",s[u].l,s[u].r,s[u].s1,s[u].s2,s[u].z,s[u].z1,s[u].lazy);
        char ss[5];
        scanf("%s",ss);
        if (ss[0]=='I') 
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            add(1,a,b,c);
        }
        else 
        {
            int a,b;
            scanf("%d%d",&a,&b);
            find (1,a,b);
            printf("%d\n",z);
            /*for (int u=1;u<=num;u++) printf("%d %d %d %d z:%d z1:%d lazy:%d\n",s[u].l,s[u].r,s[u].s1,s[u].s2,s[u].z,s[u].z1,s[u].lazy);
            printf("\n");*/
            add(1,z1,z1,-z);
        }
    }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值