• ACM-T3分块


                                                     ty的难题

    题目背景

          国民男神ty又遇到了一个小难题,他在和xqj大神的争论中(谁更强),ty表示自己不会这个问题(装弱),于是他将这个问题交给了身为ty小迷弟(妹)的你。

    题目描述:给一个长为n的数列,以及n次操作。每次操作均有一串字符和3个数字组成(c,l,r,x);有两种操作:将区间l~r加上x,询问区间l,r中比x小的

    最大元素(若不存在则输出impossible),对应每个操作,字符分别为‘change’,‘query’。

    样例输入

    3

    1 2 3

    query 1 2 2

    change 1 2 1

    query 1 2 2

    样例输出

    1

    impossible

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<set>
    using namespace std;
    int blo,bl[100005],v[100005],n,lazy[1005];
    long long read()
    {
        long long 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;
    }
    set<int> st[1005];
    void clear(int x)
    {
        st[x].clear();
        for(int i=(x-1)*blo+1;i<=min(n,x*blo);i++)
        st[x].insert(v[i]);
        //sort(st[x].begin(),st[x].end());
    }
    void change(int l,int r,int val)
    {
        for(int i=l;i<=min(bl[l]*blo,r);i++)
        v[i]+=val;
        clear(bl[l]);
        if(bl[l]!=bl[r])
        {
            for(int i=r;i>=(bl[r]-1)*blo+1;i--)
            v[i]+=val;
            clear(bl[r]);
        }
        for(int i=bl[l]+1;i<=bl[r]-1;i++)
        lazy[i]+=val;
    }
    void query(int l,int r,int x)
    {
        int ans=-1;
        for(int i=l;i<=min(bl[l]*blo,r);i++)
        if(v[i]+lazy[bl[l]]<x)
        ans=max(ans,v[i]+lazy[bl[l]]);
        if(bl[l]!=bl[r])
        {
            for(int i=r;i>=(bl[r]-1)*blo+1;i--)
            if(v[i]+lazy[bl[r]]<x)
            ans-max(ans,v[i])+lazy[bl[r]];
        }
        for(int i=bl[l]+1;i<=bl[r]-1;i++)
        {
            int c=x-lazy[i];
            set<int>::iterator it1=st[i].lower_bound(c);
            if(st[i].begin()==it1) continue;
            it1--;
            ans=max(ans,*it1+lazy[i]);
        }
        if(ans==-1) printf("impossible
    ");else
        printf("%d
    ",ans);
    }
    int main()
    {    n=read();
        blo=sqrt(n);
        for(int i=1;i<=n;i++)
        v[i]=read(),bl[i]=(i-1)/blo+1,st[bl[i]].insert(v[i]);
        //for(int i=1;i<=bl[n];i++)
        //sort(st[i].begin(),st[i].end());
        char char_[10];
        for(int i=1;i<=n;i++)
        {
            scanf("%s",&char_);
            if(char_[0]=='c')
            {
                int l,r,val;
                scanf("%d %d %d",&l,&r,&val);
                change(l,r,val);
            }else{
                int l,r,x;
                scanf("%d %d %d",&l,&r,&x);
                query(l,r,x);
            }
        }
        return 0;
    }

     这个常数有点大,1100000的数据得3秒。

    每一次clear都更新整个块

    用set的性质可以直接改(删掉原来的,加上新的)

    但是这种写法貌似在这种题目里有问题(可能在一个块里前面有一个2,后面有一个2,现在将后面那个2改为3,在set中前面那个2会变成3),所以题解有问题!

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<set>
    using namespace std;
    int blo,bl[100005],v[100005],n,lazy[1005];
    long long read()
    {
        long long 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;
    }
    set<int> st[1005];    
    void change(int l,int r,int val)
    {
    for(int i=l;i<=min(bl[l]*blo,r);i++)
    {
    st[bl[l]].erase(v[i]);
    v[i]+=val;
    st[bl[r]].insert(v[i]);
    } 
    if(bl[l]!=bl[r])
    {
        for(int i=r;i>=(bl[r]-1)*blo+1;i--)
        {
            st[bl[r]].erase(v[i]);
            v[i]+=val;
            st[bl[r]].insert(v[i]);
        }
    }    
    for(int i=bl[l]+1;i<=bl[r]-1;i++)
    lazy[i]+=val;
    }
    void query(int l,int r,int x)
    {
        int ans=-1;
        for(int i=l;i<=min(bl[l]*blo,r);i++)
        if(v[i]+lazy[bl[l]]<x)
            ans=max(ans,v[i]+lazy[bl[l]]);
        if(bl[l]!=bl[r])
        {    
            for(int i=r;i>=(bl[r]-1)*blo+1;i--)
            if(v[i]+lazy[bl[r]]<x)
            ans=max(ans,v[i])+lazy[bl[r]];
        }
    for(int i=bl[l]+1;i<=bl[r]-1;i++)
    {
    int c=x-lazy[i];
    set<int>::iterator it1=st[i].lower_bound(c);
    if(st[i].begin()==it1) continue;
    it1--;
    ans=max(ans,*it1+lazy[i]);
    }
    if(ans==-1) printf("impossible
    ");else
    printf("%d
    ",ans);
    }
    int main()
    { n=read();
    blo=sqrt(n);
    for(int i=1;i<=n;i++)
    v[i]=read(),bl[i]=(i-1)/blo+1,st[bl[i]].insert(v[i]);
    //for(int i=1;i<=bl[n];i++)
    //sort(st[i].begin(),st[i].end());
    char char_[10];
    for(int i=1;i<=n;i++)
    {
    scanf("%s",&char_);
    if(char_[0]=='c')
    {
    int l,r,val;
    scanf("%d %d %d",&l,&r,&val);
    change(l,r,val);
    }else{
    int l,r,x;
    scanf("%d %d %d",&l,&r,&x);
    query(l,r,x);
    }
    }
    return 0;
    } 

     但是这样的话set的性质大部分已经不用,直接用vector常数更小

    #include<map>
    #include<set>
    #include<cmath>
    #include<stack>
    #include<queue>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #define mod 998244353
    #define pi acos(-1)
    #define inf 0x7fffffff
    #define ll long long
    using namespace std;
    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;
    }
    int n,blo;
    int v[100005],bl[100005],atag[100005];
    set<int>st[105];
    void add(int a,int b,int c)
    {
        for(int i=a;i<=min(bl[a]*blo,b);i++)
        {
            v[i]+=c;
        }
        st[bl[a]].clear();
        for(int i=(bl[a]-1)*blo+1;i<=min(bl[a]*blo,n);i++)
        st[bl[a]].insert(v[i]);
        if(bl[a]!=bl[b])
        {
            for(int i=(bl[b]-1)*blo+1;i<=b;i++)
            {
                v[i]+=c;  
            }
            st[bl[b]].clear();
            for(int i=(bl[b]-1)*blo+1;i<=min(bl[b]*blo,n);i++)
            st[bl[b]].insert(v[i]);
        }
        for(int i=bl[a]+1;i<=bl[b]-1;i++)
            atag[i]+=c;
    }
    int query(int a,int b,int c)
    {
        int ans=-1;
        for(int i=a;i<=min(bl[a]*blo,b);i++)
        {
            int val=v[i]+atag[bl[a]];
            if(val<c)ans=max(val,ans);
        }
        if(bl[a]!=bl[b])        
            for(int i=(bl[b]-1)*blo+1;i<=b;i++)        
            {
                int val=v[i]+atag[bl[b]];
                if(val<c)ans=max(val,ans);
            }
        for(int i=bl[a]+1;i<=bl[b]-1;i++)
        {
            int x=c-atag[i];
            set<int>::iterator it=st[i].lower_bound(x);
            if(it==st[i].begin())continue;
            --it;
            ans=max(ans,*it+atag[i]);
        }
        return ans;
    }
    int main()
    {
        n=read();blo=1000;
        for(int i=1;i<=n;i++)v[i]=read();
        for(int i=1;i<=n;i++)
        {
            bl[i]=(i-1)/blo+1;
            st[bl[i]].insert(v[i]);
        }
        for(int i=1;i<=n;i++)
        {
            int f=read(),a=read(),b=read(),c=read();
            if(f==0)add(a,b,c);
            if(f==1)printf("%d
    ",query(a,b,c));
        }
        return 0;
    }
  • 相关阅读:
    如何使用xshell在阿里云服务器上安装tomcat
    如何使用Xshell连接阿里云服务器
    jQuery封装ajax的使用方法
    ES6新增语法
    数组坍塌原理
    JavaScript冒泡排序、选择排序、数组去重
    JS循环嵌套的执行原理
    分栏布局
    如何实现两列固定与一列自适应
    CSS过渡、动画及变形的基本属性与运用
  • 原文地址:https://www.cnblogs.com/dancer16/p/6882025.html
Copyright © 2020-2023  润新知