zoukankan      html  css  js  c++  java
  • 分块入门

    题目均来自于http://hzwer.com/8053.html

    评测可以在自己文件夹中分块入门里评测,小Z搞了一套题目,应该是可以的,212上有。

    接下来进入主题,分块在信息学中是比较非常重要的

    T1:Z的课堂检测(test)

    (test.cpp/c/pas)

    题目描述

    大家都知道小Z的课总是十分快的(鬼知道为什么),然后我们阿M同学总是在上课时处于神游状态亦或是休眠状态,所以她对小Z到底讲了什么是一无所知。然而,小Z总是很坏地打断阿M的休眠状态,并问她问题。作为阿M的开黑好伙伴,你当然不希望阿M同学翻车(不然下一个回答问题的人就是你啦)。所以你需要编写个程序帮助阿M求小Z对于知识点到底讲的档次有多深。已知小Z在课上总会扯到涉及到N个知识点,小Z会进行M个动作(讲课或是提问阿M)。由于小Z比较古灵精怪,所以小Z的讲课时只会讲连续的知识点,并且对于这段区间内的知识点都提升一样的档次。而且,小Z也比较懒,所以小Z只会问阿M关于某一个知识点的了解程度。

    输入描述

    第一行读入N,表示小Z要涉及到N个知识点

    第二行读入A[1],A[2]……A[N-1],A[N]表示小Z上几节课已经把第i个知识点的 难度提升到A[i]的难度

    第三行读入M,表示小Z要进行M个动作

    接下来M行,读入Choice

    Choice=1,则表示小Z要讲课啦

    接下来读入L,R,X 表示小Z要对LR这些连续的知识点提升难度X

    Choice=2,则表示小Z要提问啦

    接下来读入K,表示小Z问阿MK个知识点他已经讲到哪个难度了

    输入描述

    每行输出一个数表示阿M应该回答的正确答案

    样例输入1

    10

    1 2 3 4 5 6 7 8 9 10

    5

    1 2 3 4

    2 3

    1 3 4 5

    2 5

    1 5 8 5

    样例输出1

    7

    5

    样例输入2

    7

    5 3 7 7 5 8 5

    9

    1 2 7 -1

    2 1

    2 2

    1 2 3 1

    1 2 7 2

    2 2

    1 3 3 -1

    2 3

    2 1

    样例输出2

    5

    2

    5

    8

    5

    数据范围

    对于50%的数据,N<=1000,M<=1000

    对于100%的数据,N<=100000,M<=100000 |X|<=50000

    |A[i]|<=50000;

    压缩的题意就是给你一列数,支持区间修改和单点查询,线段树可以做,线段树用的也是分块的思想,这里用n^1/2的思想来做,是操作为O(√n)查询为O(1),和线段树一样的是,也是记录一个lazy标记,这样就可了。

    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<iostream>
    #include<cstring>
    #include<string>
    
    using namespace std;
    const int MAXN=100007;
    
    int n,m,blo,flag;
    long long a[MAXN],bl[MAXN],a_flag[MAXN];
    
    long long min(long long a,long long b)
    {
        if (a>b) return b;
        else return a;
    }
    void add(int l,int r,int z)
    {
        for (int i=l;i<=min(r,bl[l]*blo);i++)
            a[i]+=z;
        if (bl[l]!=bl[r])
        {
            for (int i=(bl[r]-1)*blo+1;i<=r;i++)
                a[i]+=z;
        }    
        for (int i=bl[l]+1;i<=bl[r]-1;i++)
            a_flag[i]+=z;    
    }
    int main()
    {
    //    freopen("test.in","r",stdin);
    //    freopen("test.out","w",stdout);
        
        scanf("%d",&n);
        blo=(int)sqrt(n);
        for (int i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
            bl[i]=(i-1)/blo+1;
        }
        scanf("%d",&m);
        int x,y,z;
        for (int i=1;i<=m;i++)
        {
            scanf("%d",&flag);
            if (flag==1)
            {
                scanf("%d%d%d",&x,&y,&z);
                add(x,y,z);
            }
            else 
            {
                scanf("%d",&x);
                long long ans=a[x]+a_flag[bl[x]];
                printf("%lld
    ",ans);
            }
        }
    }

    T2

    M的简单题(easy.pas/.cpp/.c)

    时间限制:3s 内存限制:128MB

    题目描述:

    M是某知名高中的学生,有一天,他请他的n个同学吃苹果,同学们排成一行,且手中已经有一些苹果。为了表示他的大方,有时他会给lr的同学x个苹果,但为了了解分配的情况,有时他会询问lr的同学中拥有的苹果数小于x的人的个数。现在,小M想让你帮他解决这道简单题。

    输入格式:

    第一行:两个整数n,m表示n个同学,m组询问。

    第二行:n个数,a[1],a[2]...a[n]a[i]表示第i个同学一开始手中的苹果数。(0<=a[i]<=3e4)

    3~m+2行:每行表示一组询问,格式为C l r x表示给lr的同学x个苹果,或者Q l r x表示询问lr的同学中拥有的苹果数小于x的人的个数。(1<=l<=r<=n,0<=x<=3e4)

    输出格式:

    每行一个数,输出lr的同学中拥有的苹果数小于x的人的个数。

    样例输入1

    5 5

    1 6 3 2 3

    Q 1 3 3

    C 1 2 2

    Q 3 4 3

    C 2 3 1

    Q 2 3 4

    样例输出1

    1

    1

    0

    样例输入2

    5 4

    2 3 1 3 4

    C 4 5 3

    C 1 5 1

    C 2 3 2

    Q 1 3 4

    样例输出2

    1

    数据范围:

     

    N

    M

    1~3

    1000

    1000

    4~5

    30000

    1000

    6~10

    30000

    30000

    压缩题意:给出一个长为n的数列,以及n个操作,操作涉及区间加法,询问区间内小于某个值x的元素个数。


    可以看出这题的数据范围比上题小,因为出题人的代码也卡不过100000,而且时限开了3s,不过小Z(212)上可以1s卡过,评测机比较快吧。

    说一下思路:区间加法,整个块中和打lazy标记没什么不同,但是另外的话需要重构,一次复杂度O(2√n log √n+√n);

          区间查询,整个块中用二分,不完整块内用暴力,复杂度O(√n log √n+2√n);

          简单提示:可以用vector降低代码复杂度

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    #include<cstring>
    #include<string>
    
    typedef long long LL;
    using namespace std;
    const int MAXN=30007;
    
    int n,m,blo,flag;
    LL a[MAXN],bl[MAXN],a_flag[MAXN];
    vector<LL>zhi[202];
    char c[10];
    
    void init()
    {
        memset(a,0,sizeof(a));
        memset(bl,0,sizeof(bl));
        memset(a_flag,0,sizeof(a_flag));
        for (int i=0;i<=201;i++)
            zhi[i].clear();
    }
    LL min(LL a,LL b)
    {
        if (a>b) return b;
        else return a;
    }
    void reset(int x)
    {
        zhi[x].clear();
        for (int i=(x-1)*blo+1;i<=min(n,x*blo);i++)
            zhi[x].push_back(a[i]);
        sort(zhi[x].begin(),zhi[x].end());    
    }
    void add(int l,int r,int z)
    {
        for (int i=l;i<=min(r,bl[l]*blo);i++)
        {
            a[i]+=z;
        }
        reset(bl[l]);
        if (bl[l]!=bl[r])
        {
            for (int i=(bl[r]-1)*blo+1;i<=r;i++)
                a[i]+=z;
            reset(bl[r]);    
        }
        for (int i=bl[l]+1;i<=bl[r]-1;i++)
            a_flag[i]+=z;
    }
    int query(int l,int r,int z)
    {
        int res=0;
        for (int i=l;i<=min(r,bl[l]*blo);i++)
            if (a[i]+a_flag[bl[i]]<z) res++;
        if (bl[l]!=bl[r])
        {
            for (int i=(bl[r]-1)*blo+1;i<=r;i++)
                if (a[i]+a_flag[bl[i]]<z) res++;
        }    
        for (int i=bl[l]+1;i<=bl[r]-1;i++)
        {
            int x=z-a_flag[i];
            res+=lower_bound(zhi[i].begin(),zhi[i].end(),x)-zhi[i].begin();
        }
        return res;
    }
    int main()
    {
        //freopen("easy.in","r",stdin);
        //freopen("easy.out","w",stdout);
        
        init();
        scanf("%d%d",&n,&m);
        blo=(int)sqrt(n);
        for (int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            bl[i]=(i-1)/blo+1;
            zhi[bl[i]].push_back(a[i]);
        }
        for (int i=1;i<=bl[n];i++)
            sort(zhi[i].begin(),zhi[i].end());
        int x,y,z;    
        for (int i=1;i<=m;i++)
        {
            scanf("%s",c);
            if (c[0]=='C')
            {
                scanf("%d%d%d",&x,&y,&z);
                add(x,y,z);
            }
            else 
            {
                scanf("%d%d%d",&x,&y,&z);
                printf("%d
    ",query(x,y,z));
            }
        }    
    }

     T3

    ty的难题(ty.cpp)

    时间限制: 1 Sec  内存限制: 128 MB

    题目描述

    题目背景:

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

    题目描述:

    给一个长为n的数列,以及n次操作。每次操作均有一个操作3个数字组成(operate,l,r,x操作共有两种

    0l~r区间每个数加上x

    1询问l~r区间内小于某个值x的前驱(比其小的最大元素),若不存在则输出-1

    输入

    第一行:一个整数n,表示数列长为n,以及有n次操作。

    2~n+1行:每行四个数:operate,l,r,x,分别表示操作的类型、区间l~r,和加入的元素x

    输出

    n行,每行一个数,输出lr区间内小于某个值x的前驱(比其小的最大元素)。

    样例输入

    3

    1 2 3

    1 1 2 2

    0 1 2 1

    1 1 2 2

     

    样例输出

    1
    -1

    提示

    数据范围:

    n<=5000

    10% n<=10

    30% n<=100

    60% n<=1000

    100% n<=5000

    压缩题意:给出一个长为n的数列,以及n个操作,操作涉及区间加法,询问区间内小于某个值x的前驱(比其小的最大元素)。

    题解:这道题,和上题十分类似,求小于某个值x的前驱,在每个块内,可以找块内x的前驱,怎么找比较快速呢?可以输入时存入vector里,然后sort排序,这样便可以二分查找了。块内一次查找为lg√n,其余地方暴力;修改的话,完整块里打标记,不完整块加入后重构一次O(√n log √n)。

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<string>
    #include<set>
    
    using namespace std;
    const int MAXN=100007;
    
    int n,blo;
    long long a[MAXN],bl[MAXN],a_flag[MAXN];
    char c[11];
    
    set<long long>f[407];
    
    long long max(long long a,long long b)
    {
        return a>b?a:b;
    }
    long long min(long long a,long long b)
    {
        return a>b?b:a;
    }
    void init()
    {
        for (int i=0;i<=402;i++)
            f[i].clear();
        memset(a,0,sizeof(a));
        memset(bl,0,sizeof(bl));
        memset(a_flag,0,sizeof(a_flag));
    }
    void reset(int x)
    {
        f[x].clear();
        for (int i=(bl[x]-1)*blo+1;i<=min(n,bl[x]*blo);i++)
            f[x].insert(a[i]);
    }
    void add(int l,int r,int z)
    {
        for (int i=l;i<=min(r,bl[l]*blo);i++)
        {
            a[i]+=z;
        }
        reset(bl[l]);
        if (bl[l]!=bl[r])
        {
            for (int i=(bl[r]-1)*blo+1;i<=r;i++)
            {
                a[i]+=z;
            }
            reset(bl[r]);
        }
        for (int i=bl[l]+1;i<=bl[r]-1;i++)
            a_flag[i]+=z;
    }
    long long query(int l,int r,int z)
    {
        long long ans=-MAXN;
        for (int i=l;i<=min(bl[l]*blo,r);i++)
        {
            if (a[i]+a_flag[bl[i]]<z) ans=max(ans,a[i]+a_flag[bl[i]]);
        }
        if (bl[l]!=bl[r])
        {
            for (int i=(bl[r]-1)*blo+1;i<=r;i++)
            {
                if (a[i]+a_flag[bl[i]]<z) ans=max(ans,a[i]+a_flag[bl[i]]);
            }
        }
        for (int i=bl[l]+1;i<=bl[r]-1;i++)
        {
            long long val=z-a_flag[i];
            set<long long>::iterator pos=f[i].lower_bound(val);
            //cout<<*pos<<endl;
            if (pos==f[i].begin()) continue;
            pos--;
            ans=max(ans,*pos+a_flag[i]);//pos是一个指针,所以加*为值
        }
        return ans;
    }
    int main()
    {
        
        init();
        scanf("%d",&n);
        blo=(int)sqrt(n);
        for (int i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
            bl[i]=(i-1)/blo+1;
            f[bl[i]].insert(a[i]);
        }
        int x,y,z;
        for (int i=1;i<=n;i++)
        {
            scanf("%s%d%d%d",c,&x,&y,&z);
            if (c[0]=='1')
            {
                long long ans=query(x,y,z);
                if (ans==-MAXN) printf("-1
    ");
                else printf("%lld
    ",ans);
            }
            else 
            {
                add(x,y,z);
            }
        }
        
    }

    lower_bound为大于等于的第一个值

    upper_bound为大于第一个值,等于最后一个值。

    T4

    分块入门4

    Description

    给定一个长为n的序列,要求支持m次下列操作

    1.区间加法,即对一段区间内的数整体加上一个数val

    2.区间求和,即输出一段区间内的数的和

    Input

    *第一行:两个整数N,M

    *第二行:N个整数,a[i]表示原数列中的第i个数

    *3~m+2行:每行格式如下:ADD x y val SUM x y

    Output

    对于每一行询问,输出其和

    Sample Input

    10 5

    1 2 3 4 5 6 7 8 9 10

    ADD 1 5 3

    SUM 2 6

    ADD 4 7 2

    ADD 2 6 1

    SUM 1 10

    Sample Output

    34

    83

    HINT

    数据范围

    1<=N,M<=100000

    1<=x,y<=N,-100000<=val<=100000

    -100000<=A[i]<=100000

    注意输入输出速度    x不一定<=y

    题解:陆浩琪大神出的题,题目精简,题意明了,简单易懂。

    学过线段树的朋友一定打过这道题,nlogn 十分快,而这里分块的话复杂度更高

    n√n 但是谁让是分块入门练习。

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<string>
    
    using namespace std;
    typedef long long ll;
    const int MAXN=100007;
    
    int n,m,blo;
    ll a[MAXN],a_flag[407],sum[407];
    int bl[MAXN];
    char c[11];
    
    void init()
    {
        memset(a,0,sizeof(a));
        memset(bl,0,sizeof(bl));
        memset(a_flag,0,sizeof(a_flag));
        memset(sum,0,sizeof(sum));
    }
    void add(int l,int r,int z)
    {
        for (int i=l;i<=min(r,bl[l]*blo);i++)
        {
            a[i]+=z;
            sum[bl[i]]+=z;
        }
        if (bl[l]!=bl[r])
        {
            for (int i=(bl[r]-1)*blo+1;i<=r;i++)
            {
                a[i]+=z;
                sum[bl[i]]+=z;
            }
        }
        for (int i=bl[l]+1;i<=bl[r]-1;i++)
            a_flag[i]+=z;
    }
    ll query(int l,int r)
    {
        ll ans=0;
        for (int i=l;i<=min(r,bl[l]*blo);i++)
            ans+=a[i]+a_flag[bl[i]];
        if (bl[l]!=bl[r])
        {
            for (int i=(bl[r]-1)*blo+1;i<=r;i++)
                ans+=a[i]+a_flag[bl[i]];
        }
        for (int i=bl[l]+1;i<=bl[r]-1;i++)
            ans+=sum[i]+a_flag[i]*blo;
        return ans;    
    }
    int main()
    {
        init();
        scanf("%d%d",&n,&m);
        blo=(int)sqrt(n);
        for (int i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
            bl[i]=(i-1)/blo+1;
            sum[bl[i]]+=a[i];
        }
        int x,y,z;
        for (int i=1;i<=m;i++)
        {
            scanf("%s",c);
            if (c[0]=='A')
            {
                scanf("%d%d%d",&x,&y,&z);
                if (x>y) swap(x,y);
                add(x,y,z);
            }
            else
            {
                scanf("%d%d",&x,&y);
                if (x>y) swap(x,y);
                printf("%lld
    ",query(x,y));
            }
        }
    }

    T5

    田野玩游戏(game

    时间限制:1Sec 内从限制:128MB

    题目描述:

    田野大神AK所有题感觉很水,于是开始玩起了速算游戏,屏幕上出现了n个数字。它会给出一系列操作:田野帅(0)和田野强(1)。如果是0后面有两个数l,r要求区间内所有数开,如果是1后面有两个数l,r求区间内所有数之和。

    输入

    第一行输入n

    第二行输入n个数,

    第三行输入操作数q,

    后面q行操作。

    输出

    每个询问的答案加回车。

    样例输入

    10

    1 2 3 4 5 6 7 8 9 10

    5

    0 1 10

    1 1 10

    1 1 5

    0 5 8

    1 4 8

    样例输出

    19

    7

    6

    提示

    对于50%的数据n<=10000m<=10000,

    对于100%的数据n<=100000m<=100000,

    田野说你们太菜,保证中间过程不超出longlong

    压缩题意:给出一个长为n的数列,以及n个操作,操作涉及区间开方,区间求和。

    题解:这题因为是不断开方,所以是log*,可以发现,一个int40亿的数开6-7次可以化为1

    而0,或者1开方后不变,这是一条非常重要的性质。因此出现了一种思路,可以开始时暴力,然后如果整个块都为1或0了就标记掉,这样暴力复杂度是不高的。

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<string>
    #include<cstring>
    
    using namespace std;
    const int MAXN=100007;
    const int MAXN_BLO=401;
    
    int n,m,blo;
    long long a[MAXN],bl[MAXN],flag[MAXN_BLO];
    long long sum[MAXN_BLO];
    int con;
    
    void init()
    {
        memset(flag,0,sizeof(flag));
        memset(sum,0,sizeof(sum));
        memset(a,0,sizeof(a));
        memset(bl,0,sizeof(bl));
    }
    long long min(long long a,long long b)
    {
        if (a>b) return b;
        else return a;
    }
    void solve_sqrt(int x)
    {
        if (flag[x]==1) return;
        flag[x]=1;
        sum[x]=0;
        for (int i=(x-1)*blo+1;i<=min(x*blo,n);i++)
        {
            a[i]=(int)sqrt(a[i]);
            sum[x]+=a[i];
            if (a[i]>1) flag[x]=0;
        }
    }
    void sqrT(int l,int r)
    {
        if (!flag[bl[l]]) 
        for (int i=l;i<=min(r,bl[l]*blo);i++)
        {
            sum[bl[i]]-=a[i];
            a[i]=(int)sqrt(a[i]);
            sum[bl[i]]+=a[i];
        }                     
        if (bl[l]!=bl[r])
        {
            if (!flag[bl[r]])
            for (int i=(bl[r]-1)*blo+1;i<=r;i++)
            {
                sum[bl[i]]-=a[i];
                a[i]=(int)sqrt(a[i]);
                sum[bl[i]]+=a[i];
            }
        }    
        for (int i=bl[l]+1;i<=bl[r]-1;i++)
        {
            solve_sqrt(i);
        }
    }
    long long query(int l,int r)
    {
        long long res=0;
        for (int i=l;i<=min(r,bl[l]*blo);i++)
            res+=a[i];
        if (bl[l]!=bl[r])
        {
            for (int i=(bl[r]-1)*blo+1;i<=r;i++)
                res+=a[i];
        }    
        for (int i=bl[l]+1;i<=bl[r]-1;i++)
            res+=sum[i];
        return res;    
    }
    int main()
    {
        freopen("game.in","r",stdin);
        freopen("game.out","w",stdout);
        
        init();
        scanf("%d",&n);
        blo=(int)sqrt(n);
        for (int i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
            bl[i]=(i-1)/blo+1;
            sum[bl[i]]+=a[i];
        }
        int x,y;
        scanf("%d",&m);
        for (int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&con,&x,&y);
            if (x>y) swap(x,y);
            if (con==0) sqrT(x,y);
            else printf("%lld
    ",query(x,y));
        }
    }

    T6

    相依为Gay

     

    时间限制:1 Sec 内存限制:128MB

     

    题目

        Y最近闲得慌,所以和好基友小Z玩起了一个巨无聊的游戏~~小Y有n根写有数字的签;小Y首先将自己的n根签随心所欲地排成一列,然后转身等着基友给自己出题。

       Z将会把另外的签放入两根现有的签之间(一次放一根),然后告诉小Y它的位置。有时在几个操作之间小Z会询问任意一根签上的数字,如果小Y回答不出来,嘿嘿嘿......他就要请小Z去食堂吃鸡......

    Y对此表示无奈,饱经沧桑的他没有这么好的记忆力,毕竟不是所有人都像田野那么强。然而,机智的他想到了你!!!现在他正黏在你身边求你为他解决这个问题,于是善良的你不得不屈尊为他编程解决这个问题。

     

    输入

    第一行,一个整数n。

    第二行,n个整数,表示初始序列。

    3行到第3+n-1行,每行表示一个操作。若为0 a b,则在第a根签前插入写有数字b的一根签;若为1 a,小Y需回答第a根签上写有的数字。

     

    输出

    每行一个数,表示询问的签上的数字。

     

    样例输入:

    8

    5 2 8 8 10 5 8 1

    0 6 0

    0 7 2

    1 4

    0 5 5

    0 9 9

    1 8

    0 9 1

    1 10

      

     

    样例输出:

    8

    2

    9  

     

    数据范围

     
       

     对于50%的数据,n<=10000;对于100%的数据,n<=100000。

     

    提示:分块

    压缩题意:给出一个长为n的数列,以及n个操作,操作涉及单点插入,单点询问,数据随机生成。

    冯哲宇的题解:

    分块,分成√n块,每块内可以放一个动态的数组,每次插入时先找到位置所在的块,再暴力插入,把块内的其它元素直接向后移动一位。但是,如果先在一个块有大量单点插入,这个块的大小会大大超过√n,那块内的暴力就没有复杂度保证了。所以,我们可以——重新分块,每根号n次插入后,重新把数列平均分一下块,重构需要的复杂度为O(n),重构的次数为√n,所以重构的复杂度没有问题,而且保证了每个块的大小相对均衡。当然,也可以当某个块过大时重构,或者只把这个块分成两半。(冯哲宇)

    作者题解:上面题解中解题思路忘记了重要的一点,数据是随机的,这一点还是比较重要的,以至于不会达到最坏的复杂度,次数我觉得可以改一下,本人编的代码是插入blo个数后重构一次,该浪费时还是应该浪费的,而不是有一个块到达2blo再修改。

    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<iostream>
    #include<cstring>
    #include<string>
    #include<vector>
    #include<map>
    
    using namespace std;
    const int MAXN=200007;
    
    int total,blo,n,rem;
    vector<int>shu[607];
    int a[MAXN],bl[MAXN],num[607];
    
    void init()
    {
        for (int i=0;i<607;i++)
            shu[i].clear();
        memset(a,0,sizeof(a));
        memset(bl,0,sizeof(bl));
        memset(num,0,sizeof(num));    
    }
    void reset()
    {
        int nn=0;
        for (int i=1;i<=total;i++)
        {
            for (int j=0;j<shu[i].size();j++)
            {
                a[++nn]=shu[i][j];
            }
        }
        
        
        init();
        
        blo=(int)sqrt(nn);
        n=nn;    
        for (int i=1;i<=n;i++)
        {
            bl[i]=(i-1)/blo+1;
            shu[bl[i]].push_back(a[i]);
            num[bl[i]]++;
        }
        total=bl[n];
    }
    void add(int x,int z)
    {
        int xx=0;
        for (int i=0;i<total;i++)
        {
            if (xx+num[i+1]>=x)
            {
                shu[i+1].insert(shu[i+1].begin()+x-xx-1,z);
                num[i+1]++;
                return;
            }
            else xx+=num[i+1];
        }
    }
    int query(int x)
    {
        int xx=0;
        for (int i=0;i<total;i++)
        {
            if (xx+num[i+1]>=x) return shu[i+1][x-xx-1];
            else xx+=num[i+1];
        }
    }
    int main()
    {
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
        
        scanf("%d",&n);blo=(int)sqrt(n);
        init();
        for (int i=1;i<=n;i++)
        {
            bl[i]=(i-1)/blo+1;
            scanf("%d",&a[i]);
            shu[bl[i]].push_back(a[i]);
            num[bl[i]]++;
        }
        total=bl[n];
        int flag,x,y;
        for (int i=1;i<=n;i++)
        {
            scanf("%d",&flag);
            if (flag==0)
            {
                scanf("%d%d",&x,&y);
                add(x,y);
                rem++;
            }
            else
            {
                scanf("%d",&x);
                printf("%d
    ",query(x));
            }
            if (rem%blo==0) reset;
        }
    }

     T7

    田野的武器(arms)

    时间限制:1秒  空间限制:256M

    题目描述

    众所周知,最强者田野拥有n把史诗级武器(初始每把武器的等级都是1),他正是靠着这几把武器打下了宇宙的半壁江山。有一天,田野看着他的几把武器,感觉它们不够强,于是他就像给他们升级。

    田野每次会选择连续的k把武器进行统一升t级,但由于升级的不确定性,每两次升级的级数t不一定相等。

    有些时候,田野会突然得到一些翻倍卡,并且田野一旦得到翻倍卡就会使用。翻倍卡的效果是选择连续的p把武器,使它们的级数变成原来的q倍。

    田野有些时候会想去打boss,他会从n把武器中选择一把,并请你告诉他这把武器的级数。

     

    输入

    输入第一行为两个整数n和m,表示田野有n把武器,接下来会发生m个事件。

    接下来m行,每行开头会有一个字母:

    S:接下来会有三个数x,y,z,表示把第x把武器到第y把武器全部升z级。

    F:接下来会有三个数x,y,z,表示把第x把武器到第y把武器的等级变成原来的z倍。

    B:接下来会有一个数x,表示田野想用第x把武器打boss,请你告诉他第x把武器的等级。

     

    输出

    对于每一个B询问各输出一行,表示那把武器的级数。

    由于答案可能很大,所以答案对10^4+7取模。

     

    样例输入

    5 4

    S 2 4 2

    F 1 3 3

    S 3 5 4

    B 3

     

    样例输出

    13

     

    数据范围

    对于50%的数据:1≤x≤y≤n≤2000,1≤m≤1000

    对于100%的数据:1≤x≤y≤n≤50000,1≤m≤20000,1≤z≤40000

    压缩题意:给出一个长为n的数列,以及m个操作,操作涉及区间乘法,区间加法,单点询问。

    题解:这道题,因为加法和乘法优先级不同,所以应该用两个tag标记,add表示加法标记,mul表示乘法标记。这样对于一个数有两种方法表示,a[i]=a[i]*mul+add或

    a[i]=(a[i]+add)*mul;

    分析这两种表示方法,

    第一种:

    加入一个数时,add上直接加很方便;乘入时,根据分配律(a[i]*mul+add)*z=a[i]*mul*z+add*z;

    可见都是十分简便的;

    第二种

    加入一个数时则因该在add上加入z/mul这样会出现小数,不简便;乘法时直接修改mul标记即可。

    通过对比分析,发现第一种方式更加简便,所以优先选择第一种方案。

    这样题目也就可以解决了,不完整块时开始时直接重构,然后再进行乘入,即可

    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<iostream>
    #include<string>
    #include<cstring>
    
    using namespace std;
    typedef long long LL;
    const int MAXN=100007;
    const int SQRT_MAXN=407;
    const int MOD=10007;
    
    int n,m,blo;
    LL a[MAXN],add[SQRT_MAXN],mul[SQRT_MAXN];
    int bl[MAXN];
    char c[10];
    
    void init()
    {
        memset(a,0,sizeof(a));
        memset(bl,0,sizeof(bl));
        memset(add,0,sizeof(add));
    }
    void reset(int x)
    {
        for (int i=(x-1)*blo+1;i<=x*blo;i++)
            a[i]=(a[i]*mul[x]+add[x])%MOD;
        add[x]=0;mul[x]=1;    
    }
    void solve(int flag,int l,int r,int z)
    {
        reset(bl[l]);
        for (int i=l;i<=min(bl[l]*blo,r);i++)
        {
            if (flag==1)
                a[i]=(a[i]+z)%MOD;
            else 
                a[i]=(a[i]*z)%MOD;    
        }
        if (bl[l]!=bl[r])
        {
            reset(bl[r]);
            for (int i=(bl[r]-1)*blo+1;i<=r;i++)
            {
                if (flag==1)
                    a[i]=(a[i]+z)%MOD;
                else
                    a[i]=(a[i]*z)%MOD;    
            }
        }
        for (int i=bl[l]+1;i<=bl[r]-1;i++)
        {
            if (flag==1)
                add[i]=(add[i]+z)%MOD;
            else 
            {
                add[i]=(add[i]*z)%MOD;
                mul[i]=(mul[i]*z)%MOD;
            }    
        }
    }
    int main()
    {
        freopen("arms.in","r",stdin);
        freopen("arms.out","w",stdout);
        
        init();
        
        scanf("%d%d",&n,&m);
        blo=(int)sqrt(n);
        for (int i=1;i<=n;i++)
        {
            a[i]=1;
            bl[i]=(i-1)/blo+1;
        }
        for (int i=1;i<=bl[n];i++)
            mul[i]=1;
        int x,y,z;
        for (int i=1;i<=m;i++)
        {
            scanf("%s",c);
            if (c[0]=='S')
            {
                scanf("%d%d%d",&x,&y,&z);
                solve(1,x,y,z);
            }
            else if (c[0]=='F')
            {
                scanf("%d%d%d",&x,&y,&z);
                solve(2,x,y,z);
            }
            else 
            {
                scanf("%d",&x);
                printf("%lld
    ",(a[x]*mul[bl[x]]+add[bl[x]])%MOD);
            }
        }
    }

    T8

    HK练习魔法

    题目描述:

    HK是一个魔法学徒。这一天,他准备练习幻化魔法,这是一个很神奇的魔法,它可以让石头变成黄金(虽然是假的)。但是小A的魔力有限,而他又想要更多的练习,所以他希望你可以帮助他。

    首先,在HK的面前有n个物体,他给每一个物体一个数字编号,然后他希望能够练习魔法m次,每次把从左往右数第l到r的物体变成编号为x的物体,他希望你能够计算出l到r的物体中有多少编号为x的物体,以防止他消耗不必要消耗的魔力。注意,在你计算出l到r的物体中有多少编号为x的物体,小A就会施展魔法,将l到r的物体变成编号为x的物体。

    输入:

    输入数据的第一行为n,代表有n个物体。第二行为n个物体的编号a[i]。第三行为m,代表有m次操作。接下来的m行均为l,r,x,代表在区间[l,r]中询问编号等于x的物体,并将这个区间的所有物体变成x。

    输出:

    输出有m行,表示区间[l,r]中编号等于x的物体的个数。

    样例输入:

    4

    7 1 7 2

    6

    3 3 7

    4 4 2

    3 3 7

    1 2 7

    3 3 7

    2 2 1

    样例输出:

    1

    1

    1

    1

    1

    0

    提示:

    50%的数据n<=1000,m<=100。

    100%的数据n<=100000,m<=10000。

    压缩题意:给出一个长为n的数列,以及n个操作,操作涉及区间询问等于一个数c的元素,并将这个区间的所有元素改为c。

    题解:这道题沈扬打的暴力居然也是比较快的,可以在4-5秒之内跑掉最快的数据,因为数据是随机生成的。回归正题,打了那么多到题,看到这题也不能说难了,整个块的话打标记,不完整的块则暴力查询即可。

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<vector>
    #include<map>
    
    using namespace std;
    const int MAXN=100007;
    
    int n,blo,m;
    int a[MAXN],bl[MAXN],tag[507];
    
    void init()
    {
        memset(a,0,sizeof(a));
        memset(bl,0,sizeof(bl));
        memset(tag,-1,sizeof(tag));
    }
    int find(int zhi,int x)
    {
        int res=0;
        for (int i=(x-1)*blo+1;i<=min(x*blo,n);i++)
        {
            if (zhi==a[i]) res++;
        }
        return res;
    }
    int query(int l,int r,int z)
    {
        int ans=0;
        for (int i=l;i<=min(r,bl[l]*blo);i++)
        {
            if (a[i]==z&&tag[bl[i]]==-1) ans++;
            if (tag[bl[i]]==z) ans++;
            a[i]=z;
        }
        if (tag[bl[l]]!=z&&tag[bl[l]]!=-1) 
        {
            for (int i=(bl[l]-1)*blo+1;i<l;i++)
                a[i]=tag[bl[l]];
            tag[bl[l]]=-1;    
        }
        if (bl[l]!=bl[r])
        {
            for (int i=(bl[r]-1)*blo+1;i<=r;i++)
            {
                if (a[i]==z&&tag[bl[i]]==-1) ans++;
                if (tag[bl[i]]==z) ans++;
                a[i]=z;
            }    
            if (tag[bl[r]]!=z&&tag[bl[r]]!=-1) 
            {
                for (int i=r+1;i<=min(n,bl[r]*blo);i++)
                    a[i]=tag[bl[r]];
                tag[bl[r]]=-1;
            }
        }
        for (int i=bl[l]+1;i<=bl[r]-1;i++)
        {
            if (tag[i]!=-1)
            {
                if (tag[i]==z)
                    ans+=blo;
            }
            else ans+=find(z,i);
            tag[i]=z;
        }
        return ans;
    }
    int main()
    {
        freopen("hk.in","r",stdin);
        freopen("hk.out","w",stdout);
        
        init();
        scanf("%d",&n);blo=(int)sqrt(n);
        for (int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            bl[i]=(i-1)/blo+1;
        }
        scanf("%d",&m);
        int x,y,z;
        for (int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            printf("%d
    ",query(x,y,z));
        }
    }
  • 相关阅读:
    JAVA基础——编程练习(二)
    JAVA基础——面向对象三大特性:封装、继承、多态
    JVM内存
    50. Pow(x, n) (JAVA)
    47. Permutations II (JAVA)
    46. Permutations (JAVA)
    45. Jump Game II (JAVA)
    43. Multiply Strings (JAVA)
    42. Trapping Rain Water (JAVA)
    41. First Missing Positive (JAVA)
  • 原文地址:https://www.cnblogs.com/fengzhiyuan/p/6908322.html
Copyright © 2011-2022 走看看