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));
        }
    }
  • 相关阅读:
    JavaScript构造函数学习笔记
    JavaScript面向对象编程学习笔记
    Maven学习--Maven项目目录、Maven坐标
    Maven学习--安装说明
    Watir问题---gem install watir-classic报错: ERROR: Error installing watir-classic: ERROR: Failed to build gem native extension.
    TestLink问题 —图表中文乱码
    TeskLink—增加一种需求类型(业务流程)(version1.9.8)
    Watir问题--LoadError: cannot load such file -- watir-classic
    Watir--Ruby + Watir环境安装
    Watir问题--Selenium::WebDriver::Error::WebDriverError: unexpected response, code=502, content-type="text/html"
  • 原文地址:https://www.cnblogs.com/fengzhiyuan/p/6908322.html
Copyright © 2011-2022 走看看