线段树合集

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/monochrome00/article/details/84564046

POJ - 2528 - Mayor's posters

题目链接<http://poj.org/problem?id=2528>


题意:

在范围为1e9的瓷砖上,依次覆盖1e4张海报,每张海报各不相同,问最后能看到的海报有多少张。


题解:

离散加区间修改和最后一次的区间查询。

离散有两种考虑方式:

  1. 离散边界,也就是说对于瓷砖区间r+1或者l-1。
  2. 离散区间,这样容易忽略离散后的顺序相邻但实际的位置并不相邻的情况,可以考虑在每个位置不相邻的节点中间另外加一个节点即可。

#include<cstdio>
#include<algorithm>
#include<string.h>
using namespace std;
typedef long long ll;
const int N=1e6+7;
int lazy[N<<2];
int x[N],y[N];
int a[N*2];
bool vis[N*2];
int ans=0;
void pd(int rt){
    if(lazy[rt]!=-1){
        lazy[rt<<1]=lazy[rt<<1|1]=lazy[rt];
        lazy[rt]=-1;
    }
}
void update(int L,int R,int l,int r,int rt,int tp){
    if(L<=l&&r<=R){
        lazy[rt]=tp;
        return;
    }
    pd(rt);
    int m=l+r>>1;
    if(L<=m) update(L,R,l,m,rt<<1,tp);
    if(m<R) update(L,R,m+1,r,rt<<1|1,tp);
}

void query(int l,int r,int rt){
    if(lazy[rt]!=-1){
        if(!vis[lazy[rt]]) ans++;
        vis[lazy[rt]]=true;
        return;
    }
    if(l==r)return;
    int m=l+r>>1;
    query(l,m,rt<<1);
    query(m+1,r,rt<<1|1);
}

int main()
{
    int t,n;
    scanf("%d",&t);
    while(t--){
        memset(vis,false,sizeof(vis));
        memset(lazy,-1,sizeof(lazy));
        scanf("%d",&n);
        ans=0;
        int tot=0;
        for(int i=1;i<=n;i++){
            scanf("%d%d",&x[i],&y[i]);
            y[i]+=1;
            a[++tot]=x[i];
            a[++tot]=y[i];
        }
        sort(a+1,a+1+tot);
        tot=unique(a+1,a+1+tot)-a-1;
        /*
        int tt=tot;
        for(int i=2;i<=tot;i++){
            if(a[i]>a[i-1]+1) a[++tt]=a[i-1]+1;
        }
        tot=tt;
        sort(a+1,a+1+tot);
        for(int i=1;i<=n;i++){
            x[i]=lower_bound(a+1,a+1+tot,x[i])-a;
            y[i]=lower_bound(a+1,a+1+tot,y[i])-a;
            update(x[i],y[i],1,tot,1,i);
        }
        */
        for(int i=1;i<=n;i++){
            x[i]=lower_bound(a+1,a+1+tot,x[i])-a;
            y[i]=lower_bound(a+1,a+1+tot,y[i])-a;
            update(x[i],y[i]-1,1,tot,1,i);
        }
        query(1,tot,1);
        printf("%d\n",ans);
    }
}

HDU - 4027 - Can you answer these queries?

题目链接<http://acm.hdu.edu.cn/showproblem.php?pid=4027>


题意:

区间修改:区间内的每个数变成原来数字的平方根。

区间查询:查询区间和。

数字的范围小于2^{63}


题解:

对于范围在2^{63}内的,最多做7此平方根。所以线段树每个节点最多只需要做7次的修改。记录每个区间的修改次数即可。


#include<cstdio>
#include<algorithm>
#include<string.h>
#include<cmath>
using namespace std;
typedef unsigned long long ll;
const int N=1e5+7;
ll sum[N<<2];
int num[N<<2];
void pu(int rt){
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt){
    if(l==r){
        scanf("%llu",&sum[rt]);
        return;
    }
    int m=l+r>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    pu(rt);
}
void update(int L,int R,int l,int r,int rt){
    if(L<=l&&r<=R){
        if(num[rt]>=7){
            sum[rt]=r-l+1;
            return;
        }
        num[rt]++;
    }
    if(l==r){sum[rt]=(ll)sqrt(sum[rt]);return;}
    int m=r+l>>1;
    if(L<=m) update(L,R,l,m,rt<<1);
    if(m<R) update(L,R,m+1,r,rt<<1|1);
    pu(rt);
}
ll query(int L,int R,int l,int r,int rt){
    if(L<=l&&r<=R) return sum[rt];
    int m=r+l>>1;
    ll res=0;
    if(L<=m) res+=query(L,R,l,m,rt<<1);
    if(m<R) res+=query(L,R,m+1,r,rt<<1|1);
    return res;
}
int main()
{
    int n,q;
    int cs=0;
    while(scanf("%d",&n)!=EOF){
        printf("Case #%d:\n",++cs);
        build(1,n,1);
        memset(num,0,sizeof(num));
        scanf("%d",&q);
        int a,b,c;
        while(q--){
            scanf("%d%d%d",&a,&b,&c);
            if(b>c) swap(b,c);
            if(a==0) update(b,c,1,n,1);
            else printf("%llu\n",query(b,c,1,n,1));
        }
        printf("\n");
    }
}

HDU - 1540 - Tunnel Warfare

题目链接<http://acm.hdu.edu.cn/showproblem.php?pid=1540>


题意:

一共有三种操作:破坏一个村庄,修复上一次毁坏的村庄,查询包括村庄x且没有被毁坏的最大区间。


题解:

有两种方案。

第一种可以二分村庄左边和右边最大的范围,在线段树上查询。复杂度在log*log级别。

第二种不需要二分,线段树维护两个东西:区间左端点向右的最大范围,区间右端点向左的最大范围。

如果一个区间的左端点范围能覆盖x,那么答案是这个范围加上同一层左边区间右端点的范围,反之同理。

#include<bits/stdc++.h>
using namespace std;
const int N=5e4+7;
int n,m,x;
int ll[N<<2],rl[N<<2],len[N<<2];
char s[5];
int pu(int rt){
    ll[rt]=ll[rt<<1];
    if(ll[rt<<1]==len[rt<<1]) ll[rt]+=ll[rt<<1|1];
    rl[rt]=rl[rt<<1|1];
    if(rl[rt<<1|1]==len[rt<<1|1]) rl[rt]+=rl[rt<<1];
    len[rt]=len[rt<<1]+len[rt<<1|1];
}
void build(int l,int r,int rt){
    if(l==r){
        ll[rt]=rl[rt]=len[rt]=1;
        return;
    }
    int m=l+r>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    pu(rt);
}
void update(int x,int l,int r,int rt,int val){
    if(l==r&&l==x){
        ll[rt]=rl[rt]=val;
        return;
    }
    int m=l+r>>1;
    if(x<=m) update(x,l,m,rt<<1,val);
    else update(x,m+1,r,rt<<1|1,val);
    pu(rt);
}
int query(int x,int l,int r,int rt){
    if(l==r) return ll[rt];
    if(l+ll[rt]-1>=x){
        if(l==1) return ll[rt];
        else return ll[rt]+rl[rt-1];
    }
    if(r-rl[rt]+1<=x){
        if(r==n) return rl[rt];
        else return rl[rt]+ll[rt+1];
    }
    int m=l+r>>1;
    if(x<=m) return query(x,l,m,rt<<1);
    else return query(x,m+1,r,rt<<1|1);
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF){
        stack<int>st;
        build(1,n,1);
        while(m--){
            scanf("%s",s);
            if(s[0]=='D'){
                scanf("%d",&x);
                st.push(x);
                update(x,1,n,1,0);
            }
            else if(s[0]=='R'){
                x=st.top();st.pop();
                update(x,1,n,1,1);
            }
            else{
                scanf("%d",&x);
                printf("%d\n",query(x,1,n,1));
            }
        }
    }
}

HDU - 3974 - Assign the task

题目链接<http://acm.hdu.edu.cn/showproblem.php?pid=3974>


题意:

n个员工组成一颗单向的树,每次分派给一个员工一个任务,他以及在他下面直接或间接相连的所有员工都得做这个任务。询问是哪个员工当前做的任务是什么。初始化都是-1。


题解:

映射+线段树。

每一个员工能确定一个范围,每修改一个员工就是一次区间修改。

每一个员工也有对应的一个叶子节点,每次查询就是单点查询。

叶子节点的顺序可以看成dfs序。


#include<bits/stdc++.h>
using namespace std;
const int N=5e4+7;
struct Edge{
    int v,nxt;
    Edge(int v=0,int nxt=0):v(v),nxt(nxt){}
}e[N];
struct Node{
    int l,r;
}dm[N];
int p[N],edn;
void add(int u,int v){
    e[++edn]=Edge(v,p[u]);p[u]=edn;
}
bool vis[N];
int mp[N];
int n,m,t,cs=0;
int cnt=0;
void init(int u){
    cnt++;
    mp[u]=dm[u].l=cnt;
    if(p[u]==-1){
        dm[u].r=cnt;
        return;
    }
    for(int i=p[u];~i;i=e[i].nxt){
        int v=e[i].v;
        init(v);
        dm[u].r=dm[v].r;
    }
}
int lazy[N<<2];
void pd(int rt){
    if(~lazy[rt]){
        lazy[rt<<1]=lazy[rt<<1|1]=lazy[rt];
        lazy[rt]=-1;
    }
}
void update(int L,int R,int l,int r,int rt,int val){
    if(L<=l&&r<=R){
        lazy[rt]=val;
        return;
    }
    pd(rt);
    int m=l+r>>1;
    if(L<=m) update(L,R,l,m,rt<<1,val);
    if(m<R) update(L,R,m+1,r,rt<<1|1,val);
}
int query(int x,int l,int r,int rt){
    if(~lazy[rt]||l==r){
        return lazy[rt];
    }
    int m=l+r>>1;
    if(x<=m) return query(x,l,m,rt<<1);
    else return query(x,m+1,r,rt<<1|1);
}
int main()
{
    scanf("%d",&t);
    while(t--){
        memset(vis,false,sizeof(vis));
        memset(p,-1,sizeof(p));edn=-1;
        memset(lazy,-1,sizeof(lazy));
        cnt=0;
        scanf("%d",&n);
        int u,v;
        for(int i=1;i<n;i++){
            scanf("%d%d",&v,&u);
            add(u,v);vis[v]=true;
        }
        for(int i=1;i<=n;i++){
            if(!vis[i]){
                init(i);
                break;
            }
        }
        scanf("%d",&m);
        printf("Case #%d:\n",++cs);
        char s[5];
        while(m--){
            scanf("%s",s);
            if(s[0]=='C'){
                scanf("%d",&u);
                printf("%d\n",query(mp[u],1,n,1));
            }
            else{
                scanf("%d%d",&u,&v);
                update(dm[u].l,dm[u].r,1,n,1,v);
            }
        }
    }
}

【多种修改】HDU - 4578 - Transformation

题目链接<http://acm.hdu.edu.cn/showproblem.php?pid=4578>


题意:

对于一串数字,初始化为零,一共有四种操作:

  1. 对一段区间加上一个值。
  2. 对一段区间乘上一个值。
  3. 把一段区间内每个数字变成同一个值。
  4. 对一段区间查询a_{x}^{p}+a_{x+1}^{p}+...+a_{y}^{p}, (1\leq p\leq 3)

题解:

首先考虑查询,因为p的值只有三种,可以对每种都进行维护。

乘上一个值以及变成一个值都很好维护,加法可以拆开来推:

  • 平方和:(a+c)^{2}=a^2+2ac+c^2
  • 立方和:(a+c)^{3}=a^3+c^3+3a^2c+3ac^{2}

还有一个问题,这三种操作之间会互相影响,不能简单的分开维护。可以这么考虑:(a+b)*c=a*c+b*c,也就是说每乘上一个数,区间上add数组也要乘上mul的值。一旦执行第三个操作,那么add和mul都可以回归初始化。这样子的话pushdown数组里的顺序也很明显是3->2->1。


#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+7;
const int mod=1e4+7;
int p[4][N<<2];
int add[N<<2],mul[N<<2],chg[N<<2];

void ad(int l,int r,int rt,int c){
    p[3][rt]=(p[3][rt]+3*c*p[2][rt]%mod+3*c*c%mod*p[1][rt]%mod+c*c%mod*c%mod*(r-l+1)%mod)%mod;
    p[2][rt]=(p[2][rt]+2*c*p[1][rt]%mod+c*c%mod*(r-l+1)%mod)%mod;
    p[1][rt]=(p[1][rt]+c*(r-l+1)%mod)%mod;
}
void mu(int rt,int c){
    p[1][rt]=p[1][rt]*c%mod;
    p[2][rt]=p[2][rt]*c%mod*c%mod;
    p[3][rt]=p[3][rt]*c%mod*c%mod*c%mod;
}
void cg(int l,int r,int rt,int c){
    p[1][rt]=c*(r-l+1)%mod;
    p[2][rt]=c*p[1][rt]%mod;
    p[3][rt]=c*p[2][rt]%mod;
}
void pd(int l,int r,int rt){
    int m=l+r>>1;
    if(chg[rt]){
        cg(l,m,rt<<1,chg[rt]);
        cg(m+1,r,rt<<1|1,chg[rt]);
        add[rt<<1]=0;mul[rt<<1]=1;
        add[rt<<1|1]=0;mul[rt<<1|1]=1;
        chg[rt<<1]=chg[rt<<1|1]=chg[rt];
        chg[rt]=0;
    }
    if(mul[rt]>1){
        mu(rt<<1,mul[rt]);
        mu(rt<<1|1,mul[rt]);
        add[rt<<1]=add[rt<<1]*mul[rt]%mod;
        mul[rt<<1]=mul[rt<<1]*mul[rt]%mod;
        add[rt<<1|1]=add[rt<<1|1]*mul[rt]%mod;
        mul[rt<<1|1]=mul[rt<<1|1]*mul[rt]%mod;
        mul[rt]=1;
    }
    if(add[rt]){
        ad(l,m,rt<<1,add[rt]);
        ad(m+1,r,rt<<1|1,add[rt]);
        add[rt<<1]=(add[rt<<1]+add[rt])%mod;
        add[rt<<1|1]=(add[rt<<1|1]+add[rt])%mod;
        add[rt]=0;
    }
}
void pu(int rt){
    p[1][rt]=(p[1][rt<<1]+p[1][rt<<1|1])%mod;
    p[2][rt]=(p[2][rt<<1]+p[2][rt<<1|1])%mod;
    p[3][rt]=(p[3][rt<<1]+p[3][rt<<1|1])%mod;
}
void build(int l,int r,int rt){
    add[rt]=0;mul[rt]=1;chg[rt]=0;
    p[1][rt]=p[2][rt]=p[3][rt]=0;
    if(l==r) return;
    int m=l+r>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
}
void update(int L,int R,int l,int r,int rt,int tp,int c){
    if(L<=l&&r<=R){
        if(tp==1){
            ad(l,r,rt,c);
            add[rt]=(add[rt]+c)%mod;
        }
        else if(tp==2){
            mu(rt,c);
            add[rt]=add[rt]*c%mod;
            mul[rt]=mul[rt]*c%mod;
        }
        else{
            cg(l,r,rt,c);
            add[rt]=0;mul[rt]=1;
            chg[rt]=c;
        }
        return;
    }
    pd(l,r,rt);
    int m=l+r>>1;
    if(L<=m) update(L,R,l,m,rt<<1,tp,c);
    if(m<R) update(L,R,m+1,r,rt<<1|1,tp,c);
    pu(rt);
}
int query(int L,int R,int l,int r,int rt,int c){
    if(L<=l&&r<=R) return p[c][rt];
    pd(l,r,rt);
    int m=l+r>>1,res=0;
    if(L<=m) res+=query(L,R,l,m,rt<<1,c);
    if(m<R) res+=query(L,R,m+1,r,rt<<1|1,c);
    return res%mod;
}
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF){
        if(n==0&&m==0) break;
        build(1,n,1);
        int tp,x,y,c;
        while(m--){
            scanf("%d%d%d%d",&tp,&x,&y,&c);
            if(tp<4) update(x,y,1,n,1,tp,c);
            else printf("%d\n",query(x,y,1,n,1,c));
        }
    }
}

HDU - 4614 - Vases and Flowers

题目链接<https://cn.vjudge.net/problem/45647/origin>


题意:

一共有两个操作:

  1.  从第A个位置开始放F朵花,有空位就放。直到放完或者没地方放为止。如果一朵花都没有放就输出“Can not put any one.”,否则输出第一朵放花的位置和最后一朵放花的位置。
  2. 把一段区间的花朵清空。

题解:

如果能够放完,那么就二分可以放花的区间。利用线段树查找确定第一处放花和最后一处放花的位置。


#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+7;
int sum[N<<2],lz[N<<2];
void pu(int rt){
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void pd(int l,int r,int rt){
    if(lz[rt]==1){
        int m=l+r>>1;
        sum[rt<<1]=m-l+1;
        sum[rt<<1|1]=r-m;
        lz[rt<<1]=lz[rt<<1|1]=1;
        lz[rt]=-1;
    }
    else if(lz[rt]==0){
        sum[rt<<1]=sum[rt<<1|1]=0;
        lz[rt<<1]=lz[rt<<1|1]=0;
        lz[rt]=-1;
    }
}

int q(int L,int R,int l,int r,int rt,int flag){
    if(L<=l&&r<=R){
        int res=sum[rt];
        if(flag==1) sum[rt]=r-l+1,lz[rt]=1;
        else if(flag==0) sum[rt]=0,lz[rt]=0;
        return res;
    }
    pd(l,r,rt);
    int m=l+r>>1,res=0;
    if(L<=m) res+=q(L,R,l,m,rt<<1,flag);
    if(m<R) res+=q(L,R,m+1,r,rt<<1|1,flag);
    pu(rt);
    return res;
}
int fdl(int l,int r,int rt,int x){
    if(l==r) return l;
    pd(l,r,rt);
    int m=l+r>>1;
    if(sum[rt<<1]<m-l+1&&x<=m){
    	int res=fdl(l,m,rt<<1,x);
    	if(res!=-1) return res;
    } 
    if(sum[rt<<1|1]<r-m) 
		return fdl(m+1,r,rt<<1|1,x);
    return -1;
}
int fdr(int l,int r,int rt){
    if(l==r) return l;
    pd(l,r,rt);
    int m=l+r>>1;
    if(sum[rt<<1|1]<r-m) return fdr(m+1,r,rt<<1|1);
    return fdr(l,m,rt<<1);
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m;
        scanf("%d%d",&n,&m);n--;
        memset(sum,0,sizeof(sum));
        memset(lz,-1,sizeof(lz));
        int k,x,y;
        while(m--){
            scanf("%d%d%d",&k,&x,&y);
            if(k==1){
                int ll=fdl(0,n,1,x);
                if(ll==-1){
                    printf("Can not put any one.\n");
                    continue;
                }
                int lo=x,hi=n,rr=-1;
                while(lo<=hi){
                    int mid=lo+hi>>1;
                    int num=mid-x+1-q(x,mid,0,n,1,-1);
                    if(num>=y) rr=mid,hi=mid-1;
                    else lo=mid+1;
                }
                if(rr==-1) rr=fdr(0,n,1);
                printf("%d %d\n",ll,rr);
                q(ll,rr,0,n,1,1);
            }
            else printf("%d\n",q(x,y,0,n,1,0));
        }
        printf("\n");
    }
}

HDU - 4553 - 约会安排

题目链接<http://acm.hdu.edu.cn/showproblem.php?pid=4553>


题解:

区间合并。建立两个线段树,一个线段树记录屌丝和女神的区间,一个只记录女神的区间。

线段树维护三个值:左端点向右的最大空间,右端点向左的最大空间,这个区间内的最大空间。lazy标记记录这段区间是不是空的。


#include<bits/stdc++.h>
#define ll long long
#define lc rt<<1
#define rc rt<<1|1
using namespace std;
const int N=2e5+7;
int T,cs;
char s[10];
int n,m;
struct Node{
    int l,r,len;
    int lz;
}a[2][N<<2];
void build(int l,int r,int rt){
    a[0][rt].l=a[0][rt].r=a[0][rt].len=r-l+1;
    a[1][rt].l=a[1][rt].r=a[1][rt].len=r-l+1;
    a[0][rt].lz=a[1][rt].lz=-1;
    if(l==r) return;
    int m=l+r>>1;
    build(l,m,lc);
    build(m+1,r,rc);
}
void pd(int l,int r,int rt,int o){
    if(a[o][rt].lz==-1) return;
    int tmp;
    if(a[o][rt].lz==1){
        int m=l+r>>1;
        a[o][lc].l=a[o][lc].r=a[o][lc].len=m-l+1;
        a[o][rc].l=a[o][rc].r=a[o][rc].len=r-m;
        a[o][lc].lz=a[o][rc].lz=1;
        a[o][rt].lz=-1;
    }
    else if(a[o][rt].lz==0){
        a[o][lc].l=a[o][lc].r=a[o][lc].len=0;
        a[o][rc].l=a[o][rc].r=a[o][rc].len=0;
        a[o][lc].lz=a[o][rc].lz=0;
        a[o][rt].lz=-1;
    }
}
void pu(int l,int r,int rt,int o){
    int m=l+r>>1;
    a[o][rt].l=a[o][lc].l;
    a[o][rt].r=a[o][rc].r;
    if(a[o][rt].l>=m-l+1) a[o][rt].l+=a[o][rc].l;
    if(a[o][rt].r>=r-m) a[o][rt].r+=a[o][lc].r;
    a[o][rt].len=max(a[o][lc].len,a[o][rc].len);
    a[o][rt].len=max(a[o][rt].len,max(a[o][rt].l,a[o][rt].r));
    a[o][rt].len=max(a[o][rt].len,a[o][lc].r+a[o][rc].l);
}
void update(int L,int R,int l,int r,int rt,int val,int o){
    if(L<=l&&r<=R){
        if(val) a[o][rt].l=a[o][rt].r=a[o][rt].len=r-l+1;
        else a[o][rt].l=a[o][rt].r=a[o][rt].len=0;
        a[o][rt].lz=val;
        return;
    }
    pd(l,r,rt,o);
    int m=l+r>>1;
    if(L<=m) update(L,R,l,m,lc,val,o);
    if(m<R) update(L,R,m+1,r,rc,val,o);
    pu(l,r,rt,o);
}
int query(int l,int r,int rt,int val,int o){
    if(a[o][rt].len<val) return -1;
    if(a[o][rt].l>=val) return l;
    pd(l,r,rt,o);
    int m=l+r>>1;
    if(a[o][lc].len>=val) return query(l,m,lc,val,o);
    if(a[o][lc].r+a[o][rc].l>=val) return m-a[o][lc].r+1;
    return query(m+1,r,rc,val,o);
}
int main()
{
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        printf("Case %d:\n",++cs);
        build(1,n,1);
        while(m--){
            scanf("%s",s);
            int x,y;
            if(s[0]=='D'){
                scanf("%d",&x);
                int le=query(1,n,1,x,0);
                if(le==-1) printf("fly with yourself\n");
                else{
                    printf("%d,let's fly\n",le);
                    update(le,le+x-1,1,n,1,0,0);
                }
            }
            else if(s[0]=='N'){
                scanf("%d",&x);
                int le=query(1,n,1,x,0);
                if(le==-1) le=query(1,n,1,x,1);
                if(le==-1) printf("wait for me\n");
                else{
                    printf("%d,don't put my gezi\n",le);
                    update(le,le+x-1,1,n,1,0,0);
                    update(le,le+x-1,1,n,1,0,1);
                }
            }
            else{
                scanf("%d %d",&x,&y);
                update(x,y,1,n,1,1,0);
                update(x,y,1,n,1,1,1);
                printf("I am the hope of chinese chengxuyuan!!\n");
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/monochrome00/article/details/84564046