D. Bash and a Tough Math Puzzle (线段树+dfs剪枝)

传送门

给定序列,单点修改,区间询问[l,r]中,能否最多修改一个数使得区间GCD等于x。
(询问时的修改只是想象中的修改,不会改变序列的元素)

线段树维护区间gcd这个很显然了,问题是询问如何处理呢?
刚开始的方向是找满足条件的序列,有什么规律性质,但是在纸上画了很久也没有结果。可以说方向完全错了。

正确方向是暴搜,一个数如果是所询问的x的倍数,那么就等价于这个数等于x的,因为我们可以把最终需要改的那个数修改成x, 所以我们可以直接dfs找不是x倍数的叶子节点,找到两个就不用继续了,此外如果递归到某个区间的GCD已经是x的倍数,也得剪枝剪掉。

#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
const int maxn = 5e5 + 10;
const ll mod = 998244353;
const ll inf = (ll)4e16+5;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
ll inv(ll b){
    
    if(b==1)return 1;return(mod-mod/b)*inv(mod%b)%mod;}
inline ll read()
{
    
    
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
    
    if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){
    
    x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
//数列 单点修改 区间查询[l,r]中能否最多修改1个元素 使得gcd为x
struct node 
{
    
    
    #define lc rt<<1
    #define rc rt<<1|1
    int l,r;
    int g;
}tree[maxn<<2];
inline void pushup(int rt)
{
    
    
    tree[rt].g=__gcd(tree[lc].g,tree[rc].g);
}
inline void build(int rt,int l,int r)
{
    
    
    tree[rt].l=l,tree[rt].r=r;
    if(l==r) 
    {
    
    
        scanf("%d",&tree[rt].g);
        return ;
    }
    int mid=l+r>>1;
    build(lc,l,mid);
    build(rc,mid+1,r);
    pushup(rt);
}
inline void upd(int rt,int pos,int v)
{
    
    
    int l=tree[rt].l,r=tree[rt].r;
    if(l==r) 
    {
    
    
        tree[rt].g=v;
        return ;
    }
    int mid=l+r>>1;
    if(pos<=mid) upd(lc,pos,v);
    else upd(rc,pos,v);
    pushup(rt);
}
int dfs(int rt,int vl,int vr,int v)
{
    
    
    //区间内都是v的倍数 直接返回0  因为我们修改到1处为x 这个区间就都成x了
    int l=tree[rt].l,r=tree[rt].r;
    if(r<vl || l>vr) return 0;
    if(vl<=l && r<=vr)
    {
    
    
        if(tree[rt].g % v == 0) return 0;//剪枝 这个区间gcd为v
        else if(l==r) 
        {
    
    
            return 1;
        }
    }
    int mid=l+r>>1;
    int ret=dfs(lc,vl,vr,v);
    if(ret>=2) return 2;
    return ret+dfs(rc,vl,vr,v);
}
int n,q;
int main()
{
    
    
    scanf("%d",&n);
    build(1,1,n);
    scanf("%d",&q);
    while(q--)
    {
    
    
        int f,x,y;
        scanf("%d %d %d",&f,&x,&y);
        if(f==1)
        {
    
    
            int v;
            scanf("%d",&v);          
            int cnt=dfs(1,x,y,v);//[x,y]区间内有多少个叶子节点的gcd不为v
            if(cnt>=2) puts("NO");
            else puts("YES");           
        }
        else 
        {
    
    
            upd(1,x,y);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_46030630/article/details/121217135