• [SinGuLaRiTy] 树形存储结构阶段性测试


    【SinGuLaRiTy-1011】 Copyright (c) SinGuLaRiTy 2017. All Rights Reserved.

    G2019级信息奥赛专项训练

    题目

    程序名

    时间

    内存

    测试点

    原子核研究

    atomic.cpp

    2s

    256MB

    10

    球星

    star.cpp

    2s

    256MB

    10

    跳跃

    jump.cpp

    2s

    256MB

    10

     

     

    原子核研究(atomic.cpp)

    题目描述

    最近物理学家正在研究一些新的原子,称为X族。他们发现该族中的元素非常相近,但是其质量都不相同。质量越接近的越容易相互转化。现在,物理学家已经发明了一种设备,可以对X族的元素来进行以下三种操作:
    1.generate M 产生质量为M的一个元素,如果已经存在,则该操作不产生作用。
    2.romove M 销掉质量为M的元素,如果不存在质量为M的元素,则该操作不产生作用。
    3.query 查询X族中质量最接近的两种元素的质量差的绝对值。如果此时X族中的元素个数小于2,则输出-1.
    现在,请你写一个程序来模拟该设备的操作。

    输入

    输入包含多组测试数据,第一行一个整数t,表示测试数据的组数。对于每一组数据,第一行是一个整数n,表示有n条指令。接下来n行,每行表示一条指令,如上所述。M是一个正整数,不超过100000.

    输出

    对于每组测试数据,对于其中的每一个查询操作,都要输出一个结果。注意:每一组测试数据后要输出一个空行。

    样例数据

    样例输入

    样例输出

    1
    12
    generate 1
    remove 2
    generate 1
    query
    generate 7
    query
    generate 10
    query
    generate 5
    query
    remove 7
    query

    -1
    6
    3
    2
    4

     

    STD Code

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define MAXN 100000
    #define MAXM 100000
    #define INF 0x3f3f3f3f
    typedef long long int LL;
    void Read(int &x)
    {
        x=0;char c=getchar();bool flag=0;
        while(c<'0'||'9'<c){if(c=='-')flag=1;c=getchar();}
        while('0'<=c&&c<='9'){x=x*10+c-'0';c=getchar();}
        if(flag)x=-x;
    }
    int L[MAXM*3],R[MAXM*3];
    int len[MAXM*3];
    int lval[MAXM*3],rval[MAXM*3],minval[MAXM*3];
    int sz;
    void updata(int x)
    {
        lval[x]=lval[x<<1];
        if(lval[x<<1]==len[x<<1])
            lval[x]+=lval[x<<1|1];
        rval[x]=rval[x<<1|1];
        if(rval[x<<1|1]==len[x<<1|1])
            rval[x]+=rval[x<<1];
        minval[x]=min(minval[x<<1],minval[x<<1|1]);
        if(rval[x<<1]!=len[x<<1]&&lval[x<<1|1]!=len[x<<1|1])
            minval[x]=min(minval[x],rval[x<<1]+lval[x<<1|1]);
    }
    void build(int l,int r,int x)
    {
        L[x]=l,R[x]=r;
        len[x]=r-l+1;
        lval[x]=rval[x]=len[x];
        minval[x]=INF;
        if(L[x]==R[x])
            return;
        int mid=(l+r)>>1;
        build(l,mid,x<<1);
        build(mid+1,r,x<<1|1);
        updata(x);
    }
    void insert(int pos,int x)
    {
        if(L[x]==R[x])
        {
            lval[x]=rval[x]=0;
            minval[x]=INF;
            return;
        }
        int mid=(L[x]+R[x])>>1;
        if(pos<=mid)
            insert(pos,x<<1);
        else 
            insert(pos,x<<1|1);
        updata(x);
    }
    void del(int pos,int x)
    {
        if(L[x]==R[x])
        {
            lval[x]=rval[x]=1;
            minval[x]=INF;
            return;
        }
        int mid=(L[x]+R[x])>>1;
        if(pos<=mid)
            del(pos,x<<1);
        else
            del(pos,x<<1|1);
        updata(x);
    }
    int query()
    {
        if(sz<2)
            return -1;
        else
            return minval[1]+1;
    }
    bool has[MAXM+10];
    int main()
    {
        int T,n;
        Read(T);
        while(T--)
        {
            Read(n);
            sz=0;
            build(1,MAXM,1);
            memset(has,0,sizeof(has));
            char str[10];
            int x;
            for(int i=1;i<=n;++i)
            {
                scanf("%s",str);
                if(str[0]=='g')
                {
                    Read(x);
                    if(!has[x])
                    {
                        has[x]=1,++sz;
                        insert(x,1);
                    }
                }
                else if(str[0]=='r')
                {
                    Read(x);
                    if(has[x])
                    {
                        has[x]=0;
                        --sz;
                        del(x,1);
                    }
                }
                else
                    printf("%d
    ",query());
            }
            putchar(10);
        }
        return 0;
    }

    题目分析

    首先根据题目中涉及查询,插入,删点等操作,就可以大致判断出要用树形存储结构来做。

    对于插入与删点这两个操作来说,基本就是"打板",与普通的平衡树没有什么差别。只是题目中提及“可能会删去‘不存在’的元素或插入‘存在’的元素”,最开始我没怎么在意,觉得只要在Delete函数与Insert函数里面修改一下判断条件就行了,后来却始终没有成功,调了足足有40多分钟(也就是这里耽误了太多时间)——后来发现问题在于对树的高度的更新存在问题:只要进行插入或删除操作就会更改平衡树的高度,而这显然是错误的。 后来才“迫不得已”用了一个“蠢办法”,就是设置一个bool型的has数组,去判断一个值是否存储在树中,来规避重复加/删点的问题。(当然,这个方法在最后被证明是正解)

    对于查询操作,当时其实并不觉得写起来有多大的困难:在updata函数里面顺带加一个判断去更新minval数组就行了。

    球星(star.cpp)

    题目描述

    给出球星们的能力值、年份、名字,有很多个查询,每个查询给出一个年份的范围,求出这个范围里能力值从高到低排列的前11名球员,如果能力值相同则按年份从低到高排,如果年份仍然相同,则按名字的字典序排。如果不足11个球员,就用XXX代替输出凑够11行。

    输入

    输入数据:
    第一行包含1个整数N(1<=N<=50000),表示球星的总数,接下来N行,每行描述了1个球星(year,name,ability)。0<=year<=1000000000,name不超过15个字母,0<=ability<=1000000000.
    假设没有两个人的名字相同。接下来有一个整数R,表示有R个查询。接下来R行,每行描述了一个产寻,每个查询包含2个整数(x,y),表示从第x年到第y年。(0<=x<=y<=1000000000)

    输出

    输出数据:对于每组数据,按上面描述的顺序输出最靠前的11个球员的名字,每个球员占一行。如果不足11行,则用XXX代替,凑够11行。每组数据后都有一个空行。

    样例数据

    样例输入

    样例输出

    5
    1 a 1
    2 b 2
    2 c 6
    5 e 50
    5 d 50
    2
    1 2
    5 5
    c
    b
    a
    XXX
    XXX
    XXX
    XXX
    XXX
    XXX
    XXX
    XXX
    
    d
    e
    XXX
    XXX
    XXX
    XXX
    XXX
    XXX
    XXX
    XXX
    XXX

     

     

     

     

     

     

     

     

    STD Code

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #define maxn 50000
    using namespace std;
    struct node
    {
        int l,r;
        int f[12];
    }tree[4*maxn+10];
    int f[12];
    struct d
    {
        int y,a;
        char n[20];
        bool operator <(const d &b)const
        {
            if(a==b.a)
            {
                if(y==b.y)
                {
                    int len=max(strlen(n),strlen(b.n));
                    for(int i=0;i<len;++i)
                    {
                        if(n[i]==b.n[i]) continue;
                        return n[i]>b.n[i];
                    }
                }
                return y>b.y;
            }
            return a<b.a;
        }
    }p[maxn+10];
    int n;
    bool cmp(const d &a,const d &b)
    {
        return a.y<b.y;
    }
    void uptree(int i)
    {
        int lson=i<<1,rson=(i<<1)|1;
        int cl=0,cr=0,c=0;
        while((tree[lson].f[cl]||tree[rson].f[cr])&&c<11)
        {
            if(!tree[lson].f[cl])
            {
                tree[i].f[c]=tree[rson].f[cr];
                c++;cr++;
            }
            else if(!tree[rson].f[cr])
            {
                tree[i].f[c]=tree[lson].f[cl];
                c++;cl++;
            }
            else if(p[tree[lson].f[cl]]<p[tree[rson].f[cr]])
            {
                tree[i].f[c]=tree[rson].f[cr];
                c++;cr++;
            }
            else
            {
                tree[i].f[c]=tree[lson].f[cl];
                c++;cl++;
            }
        }
        return;
    }
    void Build_tree(int i,int l,int r)
    {
        tree[i].l=l;
        tree[i].r=r;
        if(l==r)
        {
            tree[i].f[0]=l;
            return;
        }
        int m=(l+r)>>1;
        Build_tree(i<<1,l,m);
        Build_tree((i<<1)|1,m+1,r);
        uptree(i);
    }
    void Ask(int i,int l,int r)
    {
        if(tree[i].l>r||tree[i].r<l) return;
        if(l<=tree[i].l&&r>=tree[i].r)
        {
            int tf[12];
            memcpy(tf,f,sizeof f);
            memset(f,0,sizeof f);
            int c1=0,c2=0,c=0;
            while((tf[c1]||tree[i].f[c2])&&c<11)
            {
                if(!tf[c1])
                {
                    f[c]=tree[i].f[c2];
                    c++;c2++;
                }
                else if(!tree[i].f[c2])
                {
                    f[c]=tf[c1];
                    c++;c1++;
                }
                else if(p[tf[c1]]<p[tree[i].f[c2]])
                {
                    f[c]=tree[i].f[c2];
                    c++;c2++;
                }
                else
                {
                    f[c]=tf[c1];
                    c++;c1++;
                }
            }
            return;
        }
        Ask(i<<1,l,r);
        Ask((i<<1)|1,l,r);
        return;
    }
    int Find(int i,bool flag)
    {
        int L=1,R=n;
        if(!flag)
        {
            while(L<R)
            {
                int M=(L+R)>>1;
                if(p[M].y<i) L=M+1;
                else R=M;
            }
            return L;
        }
        else
        {
            while(L<R)
            {
                int M=(L+R+1)>>1;
                if(p[M].y>i) R=M-1;
                else L=M;
            }
            return L;
        }
    }
    int main()
    {
        p[0].a=-1;
        while(cin>>n)
        {
            for(int i=1;i<=n;++i)
                scanf("%d%s%d",&p[i].y,p[i].n,&p[i].a);
            sort(p+1,p+n+1,cmp);
            Build_tree(1,1,n);
            int q;
            cin>>q;
            for(int i=1;i<=q;++i)
            {
                int l,r;
                scanf("%d%d",&l,&r);
                l=Find(l,0);
                r=Find(r,1);
                Ask(1,l,r);
                for(int j=0;j<11;++j)
                {
                    if(f[j])
                        printf("%s
    ",p[f[j]].n);
                    else printf("XXX
    ");
                }
                if(i!=q) printf("
    ");
                memset(f,0,sizeof f);
            }
            memset(tree,0,sizeof tree);
        }
        return 0;
    }

    题目分析

    刚看到这道题的时候,唯一的反应就是“EXM?”实在是看不懂应该用那种算法好不好?想了想,觉得既然每个节点包含了那么多种信息,我就写个线段树+Splay好了,于是乎......写到一半发现自己写得思路已经不知所踪,那棵树呀就在转呀转呀......到了考试最后,我绝望的打了一个暴力,安慰自己:”至少,根据经验,至少能水个30分吧......“  测评时,发现竟只有一个数据点——是的,超级大数据,果断爆零。

    下来发现,似乎并没有自己想的那么复杂,用一个线段树+结构体排序,或是一个线段树+优先队列就行了。也就是说,对于线段树中的每一个节点,都塞一个排了序的数组(优先队列也是可以的)进去,维护一下,这道题就可以解决啦。(在上面的代码中我用的是结构体)

    跳跃(jump.cpp)

    事情出奇的顺利,自从高考研讨会过后,z 同学专心准备 noip2010 。可能是被 z 同学的 潇洒、帅气、阳关所吸引,zn 主动约到了 z 同学,就这样,zn 与 z 同学走到了一起~!那是 一次放假,众所周知,hz 的放假,对于不回家的同学就是能走出封闭的 hz 大门好好玩上3 个小时。Zn 刚与 z 同学吃完 kfc,他们来到了衡水唯一还算比较浪漫的地方(貌似叫什么人 民公园吧,ms 了~~)。

    题目描述

    公园中有许多木桩,每个木桩都有一个高度,活泼的小 z 同学喜欢从一个木桩跳到另 一个木桩上,zn 说太危险了,于是 z 同学让 zn 算出每一次跳跃的危险系数。小 z 每一次跳 跃的范围是一个 k*k 的矩形,危险系数为这个矩形内最高的木桩高度减去最小的。身为 oier, 你能比学数奥的 zn 算的快吗?

    输入

    第一行三个整数 n(木桩为 n*n 的矩阵)、k、b(小 zz 准备跳跃的次数) 接下来为 n*n 的矩阵,描述木桩的高度

    接下来 b 行;每行两个整数 x,y(表示 z 同学跳跃的范围的左上角为第 x 行第 y 列), 保证跳跃范围不超过矩阵的范围

    样例数据

    样例输入

    样例输出

    5 3 1

    5 1 2 6 3 

    1 3 5 2 7

    7 2 4 6 1

    9 9 8 6 5

    0 6 9 3 9

    1 2

    5

    数据范围

    对于30%的数据

    0<k<=n<=250 0<b<=100

    对于100%的数据

    0<k<-n<=250 0<b<=1000 000

    STD Code

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    int f[310][310][11];
    int g[310][310][11];
    int N,M,K;
    int min(int a,int b){return a<b?a:b;}
    int max(int a,int b){return a>b?a:b;}
    int log_2(int n){
        int t=0;
        while ((1 << (t+1))<n) t++;
        return t;
    }
    int main(){
        memset(f,63,sizeof(f));
        memset(g,0,sizeof(g));
        scanf("%d%d%d",&N,&M,&K);
        for (int i=1;i<=N;i++){
            for (int j=1;j<=N;j++){
                scanf("%d",&f[i][j][0]);
                g[i][j][0]=f[i][j][0];
            }
        }
        int w=log_2(M);
        for (int k=1;k<=w;k++){
            for (int i=1;i<=N;i++){
                for (int j=1;j<=N;j++){
                    if (i+(1 << k)-1<=N && j+(1 << k)-1<=N){
                             f[i][j][k]=min(f[i][j][k-1],f[i+(1 << (k-1))][j][k-1]);
                             f[i][j][k]=min(f[i][j][k],f[i][j+(1 << (k-1))][k-1]);
                             f[i][j][k]=min(f[i][j][k],f[i+(1 << (k-1))][j+(1 << (k-1))][k-1]);
                             g[i][j][k]=max(g[i][j][k-1],g[i+(1 << (k-1))][j][k-1]);
                             g[i][j][k]=max(g[i][j][k],g[i][j+(1 << (k-1))][k-1]);
                             g[i][j][k]=max(g[i][j][k],g[i+(1 << (k-1))][j+(1 << (k-1))][k-1]);
                    }
                }
            }
        }
        for (int t=1;t<=K;t++){
            int i,j;
            scanf("%d%d",&i,&j);
            int t1=i+M-1;
            int t2=j+M-1;
            int mi,ma;
            mi=min(f[i][j][w],f[t1-(1 << w)+1][t2-(1 << w)+1][w]);
            mi=min(mi,f[i][t2-(1 << w)+1][w]);
            mi=min(mi,f[t1-(1 << w)+1][j][w]);
            ma=max(g[i][j][w],g[t1-(1 << w)+1][t2-(1 << w)+1][w]);
            ma=max(ma,g[i][t2-(1 << w)+1][w]);
            ma=max(ma,g[t1-(1 << w)+1][j][w]);
            printf("%d
    ",ma-mi);
        }
        return 0;
    }

    题目分析

    “最大的”减去“最小的”,这不是一个区间求最值的RMQ问题吗?建一个线段树不就行了?再仔细一看,“n*n的矩阵”,Oh No,这明摆着是要你写一个树套树(矩形树)嘛! 由于此思路的代码非常“绞”,再加上时间不够,最后果断放弃去写第二题的暴力了。

    下来后,经过两天的冥思苦想,我终于想出了“还算好写”的矩形树代码,将大矩阵分解成为多个边长为2的小正方形矩阵(即2^k*2^k),在这些小矩阵中做一个RMQ问题求出最值,在随后的数据中,每输入一个矩形,就进行一次“分割为小矩形”的操作,直接比较求最值,交上去,令人感动的AC!

    Time:2017-03-25

  • 相关阅读:
    通过一个程序来理解PHP里面的抽象类【1】
    mycheckpoint 把玩手记
    Mysql 大量 unauthenticated user
    零基础学习Oracle 10G视频教程
    mysql show processlist命令 详解
    c# 中的事件
    c# 中方法签名 指的是?
    c# 中的索引
    介绍如何使用C#中的委托
    C# 语法学习笔记
  • 原文地址:https://www.cnblogs.com/SinGuLaRiTy2001/p/6606552.html
Copyright © 2020-2023  润新知