hdu4902 线段树

题意:将一段中的值大于x的修改成gcd(x,num) 将一段的值都修改成x;


解法1:开两个标记和一个最小值的记录 然后往下的时候更新最小值比x大的区间


解法2:暴力,采用离线做法,用线段树记下每次点最后被set的数值,然后对于每个数进行暴力枚举gcd修改


标记法:

#include<iostream>
#include<cstdio>
#include<algorithm>
#define maxn 111111
#define ls (rt<<1)
#define rs (rt<<1|1)
#define mid ((l+r)>>1)
using namespace std;
#define ll int
#define inf (-(1<<31))
int set[maxn<<2],gc[maxn<<2],mi[maxn<<2];

int gcd(int a,int b){
    return b==0?a:gcd(b,a%b);
}

void up(int rt){
    mi[rt]=min(mi[ls],mi[rs]);
    set[rt]=(set[ls]==set[rs]?set[ls]:inf);
}
void down(int rt){
    if(set[rt]!=inf){
        set[ls]=set[rs]=set[rt];
        mi[ls]=mi[rs]=set[rt];
        gc[ls]=gc[rs]=0;
        set[rt]=inf;
    }
    if(gc[rt]){
        gc[ls]=gcd(gc[ls],gc[rt]);
        gc[rs]=gcd(gc[rs],gc[rt]);
        mi[ls]=gcd(mi[ls],gc[rt]);
        mi[rs]=gcd(mi[rs],gc[rt]);
        gc[rt]=0;
    }
}
void build(int rt,int l,int r){
    set[rt]=inf;
    gc[rt]=0;
    if(l==r){
        scanf("%d",&mi[rt]);
        return;
    }
    build(ls,l,mid);
    build(rs,mid+1,r);
    up(rt);
}
void _set(int rt,int l,int r,int L,int R,int w){
    if(L<=l&&r<=R){
        set[rt]=mi[rt]=w;
        gc[rt]=0;
        return;
    }
    down(rt);
    if(L<=mid)_set(ls,l,mid,L,R,w);
    if(mid<R)_set(rs,mid+1,r,L,R,w);
    up(rt);
}
void _gcd(int rt,int l,int r,int L,int R,int w){
    if(L<=l&&r<=R)
        if(set[rt]!=inf||l==r){
          if(mi[rt]>w){
              if(set[rt]!=inf){
                  set[rt]=gcd(set[rt],w);
              }
                gc[rt]=gcd(gc[rt],w);
                mi[rt]=gcd(mi[rt],w);
          }
          return;
       }
    down(rt);
    if(L<=mid)_gcd(ls,l,mid,L,R,w);
    if(mid<R)_gcd(rs,mid+1,r,L,R,w);
    up(rt);
}
void query(int rt,int l,int r){
    if(l==r){
        printf("%d ",mi[rt]);return;
    }
    down(rt);
    query(ls,l,mid);
    query(rs,mid+1,r);
}
int main()
{
    int _;
    scanf("%d",&_);
    while(_--) {
        int n,m,op,l,r,w;
        scanf("%d",&n);
        build(1,1,n);
        scanf("%d",&m);
        while(m--){
            scanf("%d%d%d%d",&op,&l,&r,&w);
            if(op==1)_set(1,1,n,l,r,w);
            else if(op==2)_gcd(1,1,n,l,r,w);
        }
        query(1,1,n);
        printf("\n");
        
    }
    return 0;
}

暴力:

#include <cstdio>
#include <cstring>
#define maxn 100005
#define ls (rt<<1)
#define rs (rt<<1|1)
#define mid ((l+r)>>1)
int n,q,a[maxn],x[maxn],p,l[maxn],r[maxn],op[maxn];
int s[maxn<<2];
int gcd(int a,int b){
    return a?gcd(b%a,a):b;
}
void build(int rt,int l, int r){
    s[rt]=-1;
    if(l==r){
        s[rt]=0;
        return;
    }
    build(ls,l,mid);
    build(rs,mid+1,r);
}
void ins(int rt,int l,int r,int L,int R,int w){
    if(L<=l&&r<=R){
        s[rt]=w;
        return;
    }
    if(s[rt]!=-1){
        s[ls]=s[rt];
        s[rs]=s[rt];
        s[rt]=-1;
    }
    if(L<=mid)ins(ls,l,mid,L,R,w);
    if(mid<R)ins(rs,mid+1,r,L,R,w);
}
int query(int rt,int l, int r, int k){
    if(s[rt]!=-1)return s[rt];
    if(k<=mid)return(query(ls,l,mid,k));
    else return(query(rs,mid+1,r,k));
}
int main()
{
    int _;
    scanf("%d",&_);
    while(_--){
        scanf("%d",&n);
        build(1,1,n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        scanf("%d",&q);
        for(int i=1;i<=q;i++){
            scanf("%d%d%d%d",&op[i],&l[i],&r[i],&x[i]);
            if(op[i]==1)ins(1,1,n,l[i],r[i],i);
        }
        for(int i=1;i<=n;i++){
            p=query(1,1,n,i);
            if(p)a[i]=x[p];
            for(int j=p+1;j<=q;j++)
                if(op[j]==2&&l[j]<=i&&r[j]>=i){
                    if(a[i]>x[j]){
                        a[i]=gcd(a[i],x[j]);
                    }
                }
        }
        for(int i=1;i<=n;i++)printf("%d ",a[i]);
        printf("\n");
    }
    return 0;
}


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值