• BZOJ3211 花神游历各国 区间开根号


    花神喜欢步行游历各国,顺便虐爆各地竞赛。花神有一条游览路线,它是线型的,也就是说,所有游历国家呈一条线的形状排列,花神对每个国家都有一个喜欢程度(当然花神并不一定喜欢所有国家)。
    每一次旅行中,花神会选择一条旅游路线,它在那一串国家中是连续的一段,这次旅行带来的开心值是这些国家的喜欢度的总和,当然花神对这些国家的喜欢程序并不是恒定的,有时会突然对某些国家产生反感,使他对这些国家的喜欢度Q变为sqrt(Q)(可能是花神虐爆了那些国家的OI,从而感到乏味)。
    现在给出花神每次的旅行路线,以及开心度的变化,请求出花神每次旅行的开心值。

    输入格式
    第一行是一个整数N,表示有个国家;
    第二行有N个空格隔开的整数,表示每个国家的初始喜欢度Qi;
    第三行是一个整数M,表示有条信息要处理;
    第四行到最后,每行三个整数x,l,r,当x=1时询问游历国家到的开心值总和,也就是x=2,当时国家到中每个国家的喜欢度q变为sqrt(q)。

    输出格式
    每次时,每行一个整数。表示这次旅行的开心度

    input
    4
    1 100 5 5
    5
    1 1 2
    2 1 2
    1 1 2
    2 2 3
    1 1 4

    output
    101
    11
    11

    对于全部数据
    1<=N<=100000
    1<=M<=2*100000
    1<=l<=r<=n
    0<=Qi<=1e9

    注:建议使用 sqrt 函数,且向下取整.

    Sol1:
    看来对点修改,想到了树状数组。
    然后这种成段修改,用Bit操作还是有点烦的,但是因为发现一个正整数
    其实开不了几次根号,就会变得<=1,于是后面就不要再开根号了。
    所以还是一个个去修改吧,但可以加的优化是:
    我们可以利用并查集来快速找到某个数字(含它自身在内)右边第一个还可以
    进行开根号操作的数字。

    #include <cstring>
    #include <algorithm>
    #include <cstdio>
    #include <cmath>
    #include <cstdlib>
    using namespace std;
    typedef long long LL;
    const int N=100005;
    int n,m,fa[N],v[N],j;
    LL tree[N];
    int lowbit(int x){
        return x&-x;
    }
    void add(int x,int d){
        for (;x<=n;x+=lowbit(x))
            tree[x]+=d;
    }
    LL sum(int x){
        LL ans=0;
        for (;x>0;x-=lowbit(x))
            ans+=tree[x];
        return ans;
    }
    int getf(int k){
        return fa[k]==k?k:fa[k]=getf(fa[k]);
    }
    int main()
    {
        scanf("%d",&n);
        memset(tree,0,sizeof tree);
        for (int i=1;i<=n;i++)
    	{
            scanf("%d",&v[i]);
            add(i,v[i]);
            fa[i]=i;
        }
        fa[n+1]=n+1;
        scanf("%d",&m);
        while (m--){
            int op,a,b;
            scanf("%d%d%d",&op,&a,&b);
            if (op==1)
                printf("%lld
    ",sum(b)-sum(a-1));
            else {
            	
            	    j=getf(a);
                	while(j<=b)
            	    {
            		int v_=int(sqrt(v[j]));
                    add(j,v_-v[j]);
                    v[j]=v_;
                    if (v[j]<=1)
                    {
                    
                        fa[j]=j+1;
                        j=getf(j);
                        
                    }
                    else
                        j++;
            	
                    
                }
            }
        }
        return 0;
    }
    

      

     Sol2:思路同上,利用线段树来进行单点修改。

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <stack>
    #include <algorithm>
    #include <cstring>
    #include <climits>
    #define MAXN 2000000+10
    #define LL long long
    using namespace std;
    LL n,m,a[MAXN],num[MAXN<<2],sit[MAXN];
    int fa[MAXN];
    int j;
    int find(int x)
    {
        if(fa[x]!=x) fa[x]=find(fa[x]);
        return fa[x];
    }
    void build(int rt,int l,int r)
    {
        if(l==r)
        {
            num[rt]=a[l];
            sit[l]=rt;
            return ;
        }
        int m=(l+r)>>1;
        build(rt<<1,l,m);
        build(rt<<1|1,m+1,r);
        num[rt]=num[rt<<1]+num[rt<<1|1];
    }
    void update(int x,int l,int r,int rt)
    {
        if(l==r&&l==x)
        {
            num[rt]=sqrt(num[rt]);
            return;
        }
        int m=(l+r)>>1;
        if(x<=m) update(x,l,m,rt<<1); else
        if(x>m)  update(x,m+1,r,rt<<1|1);
        num[rt]=num[rt<<1]+num[rt<<1|1];
    }
    LL query(int L,int R,int l,int r,int rt)
    {
        if(L<=l&&r<=R)
            return num[rt];
        int m=(l+r)>>1;LL ans=0;
        if(L<=m) ans+=query(L,R,l,m,rt<<1);
        if(R>m) ans+=query(L,R,m+1,r,rt<<1|1);
        return ans;
    }
    
    int main()
    {
        scanf("%lld",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
            fa[i]=i;
        }
        fa[n+1]=n+1;
        build(1,1,n);
        scanf("%lld",&m);
        for(int i=1;i<=m;i++)
        {
            int o,a,b;
            scanf("%d%d%d",&o,&a,&b);
            if(a>b) swap(a,b);
            if(o==2)
            {
            	j=find(a);
            	while(j<=b)
            	{
            		update(j,1,n,1);
            		if(num[sit[j]]==1||num[sit[j]]==0)
            		//如果已变成1或0了,则这个数字不要再进行开根号运算了 
                        {
                 	        	fa[j]=j+1;
                                j=find(j);
                        }
                    else //否则j就只能移动它后面的一个数字上 
                         j++;
            	}
            	/*
                for(int j=find(a-1)+1;j<=find(b);)
                {   
                    update(j,1,n,1);
                    if(num[sit[j]]==1||num[sit[j]]==0)
                        fa[j-1]=j;
                    j=find(j)+1;
                }
                */
            }else
            printf("%lld
    ",query(a,b,1,n,1));
        }
        return 0;
    }
    

      

    Sol3:使用线段树来进行成段更新

    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    #include<math.h>
    using namespace std;
    #define p1 (p<<1)
    #define p2 (p<<1|1)
    const int N=100005;
    int n,m,i,p,x,y,a[N],add[N<<2];
    long long ans,t[N<<2];
    void build(int l,int r,int p)
    {
    if(l==r)
    {
       t[p]=a[l];
       if(a[l]<=1) 
          add[p]=1;
       else 
            add[p]=0;
    return;
    }
    int mid=(l+r)>>1;
    build(l,mid,p1);
    build(mid+1,r,p2);
    t[p]=t[p1]+t[p2];
    add[p]=add[p1]&add[p2];
    }
    void update(int l,int r,int x,int y,int p)
    {
    if(add[p]) return;
    if(l==r)
    {
    t[p]=(int)(sqrt(t[p]));
    if(t[p]<=1) add[p]=1;
    return;
    }
    int mid=(l+r)>>1;
    if(x<=mid)
         update(l,mid,x,y,p1);
    if(y>mid) 
         update(mid+1,r,x,y,p2);
    add[p]=add[p1]&add[p2];
    t[p]=t[p1]+t[p2];
    }
    void solve(int l,int r,int x,int y,int p)
    {
    if(x<=l&&r<=y)
    {
        ans+=t[p];
        return;
    }
    int mid=(l+r)>>1;
    if(x<=mid) solve(l,mid,x,y,p1);
    if(y>mid) solve(mid+1,r,x,y,p2);
    }
    int main()
    {
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    scanf("%d",&a[i]);
    build(1,n,1);
    scanf("%d",&m);
    for(i=1;i<=m;i++)
    {
    scanf("%d%d%d",&p,&x,&y);
    if(p==1)
    {
    ans=0;
    solve(1,n,x,y,1);
    printf("%lld
    ",ans);
    } 
    else
    update(1,n,x,y,1);
    }
    return 0;
    }
    

      Sol4:分块算法

    #include<cmath>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define ll long long
      
    int n,k,opt,l,r,c,p[50010];
    ll t[50010],w[50010];bool f[50010];
      
    void Work(int now)
    {
        w[p[now]]-=t[now];//从总和中减去当前位置上的值 
        t[now]=sqrt(t[now]);//当前位置上的值开根号 
        w[p[now]]+=t[now];//再加回去 
    }
      
    void Calc(int now)
    {
        if(f[now])//如果当前位置已全为0了 
            return;
        f[now]=true;//假设这一块全为0了,这一句非常重要,因为如果不全为0,后面会改过去的 
        w[now]=0;
        for(int i=(now-1)*k+1;i<=now*k;i++)
        {
            t[i]=sqrt(t[i]);
               w[now]+=t[i];
            if(t[i]>1)
               f[now]=false;
        }
    }
      
    void Change(int l,int r,int c)
    {
        if(p[l]==p[r])
            for(int i=l;i<=r;i++)
                Work(i);
        else{
            for(int i=l;i<=p[l]*k;i++)
                Work(i);
            for(int i=(p[r]-1)*k+1;i<=r;i++)
                Work(i);
            for(int i=p[l]+1;i<=p[r]-1;i++)
                Calc(i);
        }
    }
      
    ll Query(int l,int r)
    {
        ll ans=0;
        if(p[l]==p[r])
            for(int i=l;i<=r;i++)  //暴力统计 
                ans+=t[i];
        else{
            for(int i=l;i<=p[l]*k;i++)
                ans+=t[i];
            for(int i=(p[r]-1)*k+1;i<=r;i++)
                ans+=t[i];
            for(int i=p[l]+1;i<=p[r]-1;i++)
                ans+=w[i];
        }
        return ans;
    }
      
    int main(){
        scanf("%d",&n);k=sqrt(n);
        for(int i=1;i<=n;i++)p[i]=(i-1)/k+1;
        for(int i=1;i<=n;i++)
            scanf("%d",&t[i]),w[p[i]]+=t[i];
        for(int i=1;i<=n;i++){
            scanf("%d%d%d%d",&opt,&l,&r,&c);
            if(opt)
                printf("%lld
    ",Query(l,r));
            else //表示将位于[l,r]的之间的数字都开方
                 Change(l,r,c);
        }
        return 0;
    }
    

      

  • 相关阅读:
    SQL Server 和Oracle 12C体系结构图
    SQL Server Latch
    MSSQL SSIS agent job运行出错
    sys.processes spid 和 blocked是同一个 session id
    黑苹果导致Windows时间不对或关机不断电
    MSSQL shrink数据库
    MSSQL 死锁或阻塞检测
    MSSQL不能shrink日志
    tomcat配置https协议
    ELK windows下部署测试
  • 原文地址:https://www.cnblogs.com/cutemush/p/14691376.html
Copyright © 2020-2023  润新知