洛谷P4145上帝造题的七分钟——区间修改

Posted Zinn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷P4145上帝造题的七分钟——区间修改相关的知识,希望对你有一定的参考价值。

题目:https://www.luogu.org/problemnew/show/P4145

区间开平方,可以发现其实开几次就变成1,不需要开了,所以标记一下,每次只去开需要开的地方;

原来写的并查集跳过1或0,然而WA...

(如果a数组<原数组>开int会RE!)

改成线段树,本来想着是这一段区间和只要小于等于其长度就可以跳过了,然而仔细想想完全不是,应为可能有多个0什么的;

所以直接开bool数组标记一下就好了;

不需要pushdown,直接去修改或是跳过。

并查集:

技术分享图片
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;
int const MAXN=100005;
int n,m,a[MAXN],fa[MAXN];
ll f[MAXN];
int find(int x)
{
    if(x==fa[x])return x;
    return fa[x]=find(fa[x]);
}
void add(int x,ll y)
{
    for(;x<=n;x+=(x&-x))
        f[x]+=y;
}
void update(int x)
{
    int tmp=a[x];
    a[x]=sqrt(a[x]);
    if(a[x]==1||a[x]==0)fa[x]=find(x+1);
    add(x,a[x]-tmp);
//    for(;x<=n;x+=(x&-x))
//        f[x]-=tmp,f[x]+=a[x];
}
ll query(int x)
{
//    for(int i=1;i<=n;i++)
//        printf("%d ",a[i]);
//    printf("\n");
    ll sum=0;
    for(;x;x-=(x&-x))
        sum+=f[x];
    return sum;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        fa[i]=i;
        add(i,a[i]);
    }
//    for(int i=1;i<=n;i++)
//        printf("%lld ",f[i]);
//    printf("\n");
    fa[n+1]=n+1;
    scanf("%d",&m);
    while(m--)
    {
        int t,l,r;
        scanf("%d%d%d",&t,&l,&r);
        if(l>r)swap(l,r);
        if(t==0)
        {
            int x=find(l);
            while(x<=r)
            {
                update(x);
                x=find(x+1);
//                cout<<x<<endl;
            }
        }
        if(t==1)
        {
            ll s1=0,s2=0;
            if(l-1)s1=query(l-1);
            s2=query(r);
//            printf("s1=%lld s2=%lld\n",s1,s2);
            printf("%lld\n",s2-s1);
        }
    }
    return 0;
}

代码如下:

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;
int const MAXN=100005;
int n,m;
ll tr[MAXN<<2],a[MAXN];
bool tg[MAXN<<2];
void pushup(int nw)
{
    tr[nw]=tr[nw<<1]+tr[nw<<1|1];
    tg[nw]=(tg[nw<<1]&&tg[nw<<1|1]);
}
//void pushdown(int l,int r,int nw)
//{
//    if(l==r)
//    {
//        tr[nw]=sqrt(tr[nw]);
//        return;
//    }
//    while(lz[nw])
//    {
//        if(tr[nw]<=r-l+1)
//        {
//            lz[nw]=0;
//            break;
//        }
//        int mid=((l+r)>>1);
//        if(tr[nw<<1]>mid-l+1)pushdown(l,mid,nw<<1);
//        if(tr[nw<<1|1]>r-mid)pushdown(mid+1,r,nw<<1|1);
//        pushup(nw);
//        lz[nw]--;
//    }
//}
void update(int l,int r,int L,int R,int nw)
{
    if(tg[nw])return;
    if(l==r)
    {
        tr[nw]=(ll)sqrt(tr[nw]);
        if(tr[nw]==1||tr[nw]==0)tg[nw]=1;
        return;
    }
    int mid=((l+r)>>1);
    if(mid>=L)update(l,mid,L,R,nw<<1);
    if(mid<R)update(mid+1,r,L,R,nw<<1|1);
    pushup(nw);
}
ll query(int l,int r,int L,int R,int nw)
{
//    for(int i=1;i<=n;i++)
//        printf("%d ",a[i]);
//    printf("\n");
    ll sum=0;
    if(l>=L&&r<=R)
    {
//        pushdown(l,r,nw);
        return tr[nw];
    }
    int mid=((l+r)>>1);
    if(mid>=L)sum+=query(l,mid,L,R,nw<<1);
    if(mid<R)sum+=query(mid+1,r,L,R,nw<<1|1);
    return sum;
}
void build(int l,int r,int nw)
{
    if(l==r)
    {
        tr[nw]=a[l];
        if(a[l]==1||a[l]==0)tg[nw]=1;
        return;
    }
    int mid=((l+r)>>1);
    build(l,mid,nw<<1);
    build(mid+1,r,nw<<1|1);
    pushup(nw);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    scanf("%d",&m);
    build(1,n,1);
    while(m--)
    {
        int d,x,y;
        scanf("%d%d%d",&d,&x,&y);
        if(x>y)swap(x,y);
        if(d==0)update(1,n,x,y,1);
        if(d==1)printf("%lld\n",query(1,n,x,y,1));
    }
    return 0;
}

 

以上是关于洛谷P4145上帝造题的七分钟——区间修改的主要内容,如果未能解决你的问题,请参考以下文章

P4145 上帝造题的七分钟2 / 花神游历各国 题解

P4145 上帝造题的七分钟2 / 花神游历各国

P4145 上帝造题的七分钟2 / 花神游历各国

luogu P4514ybtoj树状数组课堂过关差分 例题6区间修改区间查询 & 上帝造题的七分钟

BZOJ 3038: 上帝造题的七分钟2线段树区间开方问题

BZOJ3038 上帝造题的七分钟2