bzoj P1058 [ZJOI2007]报表统计——solution

1058: [ZJOI2007]报表统计

Time Limit: 15 Sec  Memory Limit: 162 MB Submit: 4099  Solved: 1390 [Submit][Status][Discuss]

Description

  小Q的妈妈是一个出纳,经常需要做一些统计报表的工作。今天是妈妈的生日,小Q希望可以帮妈妈分担一些工

作,作为她的生日礼物之一。经过仔细观察,小Q发现统计一张报表实际上是维护一个可能为负数的整数数列,并

且进行一些查询操作。在最开始的时候,有一个长度为N的整数序列,并且有以下三种操作: INSERT i k 在原数

列的第i个元素后面添加一个新元素k; 如果原数列的第i个元素已经添加了若干元素,则添加在这些元素的最后(

见下面的例子) MIN_GAP 查询相邻两个元素的之间差值(绝对值)的最小值 MIN_SORT_GAP 查询所有元素中最接

近的两个元素的差值(绝对值) 例如一开始的序列为 5 3 1 执行操作INSERT 2 9将得到: 5 3 9 1 此时MIN_GAP

为2,MIN_SORT_GAP为2。 再执行操作INSERT 2 6将得到: 5 3 9 6 1 注意这个时候原序列的第2个元素后面已经

添加了一个9,此时添加的6应加在9的后面。这个时候MIN_GAP为2,MIN_SORT_GAP为1。于是小Q写了一个程序,使

得程序可以自动完成这些操作,但是他发现对于一些大的报表他的程序运行得很慢,你能帮助他改进程序么?

Input

  第一行包含两个整数N,M,分别表示原数列的长度以及操作的次数。第二行为N个整数,为初始序列。接下来

的M行每行一个操作,即“INSERT i k”,“MIN_GAP”,“MIN_SORT_GAP”中的一种(无多余空格或者空行)。

Output

  对于每一个“MIN_GAP”和“MIN_SORT_GAP”命令,输出一行答案即可。

Sample Input

3 5 5 3 1 INSERT 2 9 MIN_SORT_GAP INSERT 2 6 MIN_GAP MIN_SORT_GAP

Sample Output

2 2 1

HINT

N , M ≤500000 对于所有的数据,序列内的整数不超过5*10^8。

http://www.lydsy.com/JudgeOnline/problem.php?id=1058

                          - by bzoj




首先明确每次插入的位置可以通过简单的记录来快速得知,

然后分开考虑两种询问

1询问排序后的相邻元素间的最小差值,

如果维护排序后相邻元素间差值的话,元素的加入影响差值,应该是一个有加入有删除的操作,

而事实上由于每一次删除差值的诱因一定是新加入的元素夹在原来的两个元素之间,破坏了这两个元素间的差值,

这意味着删除的差值一定不如新产生的差值小,

于是可以视作没有删除;

那就是一个单纯比较取最小的过程了;

为了找到新产生了哪些差值,笔者这里选用了set对报表中的元素进行动态排序加二分查找;

这个询问的答案只会越来越小,最后可能到零,记得到零后记录一下,省一点多余的操作

2询问当前没有排序的序列的相邻元素的最小值,

如果维护未排序时相邻元素间差值的话,元素的加入影响差值,这里真的是一个有加入有删除的操作了;

每次在i与i+1间插入一个元素x,在维护差值的数据结构中找到abs(a[i+1]-a[i])删掉,然后加入abs(x-a[i])和abs(x-a[i+1]);

这里可以用平衡树维护

笔者这里偷懒直接用动态加点线段树做了,本来内存巨大应该过不了,遂偷偷改小数组,没想到竟然bzoj和luogu的数据都水过来了,暗爽

——现在写题解时想想,好像只要写个set维护就完事了

——结果就是道STL题啊!!!!!!!!!!!!

代码:

#include<map>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
map <int ,int >MAP;
int id_now[500010];
struct ss{
    int num,las,nex;
}ctn[1000010];
struct DT{
    int size,ch[2];
}data[10000000];
int top,tot,n,m;
bool flag;
char s[20];
inline void in(int &ans)
{
    ans=0;bool p=false;char ch=getchar();
    while((ch>‘9‘ || ch<‘0‘)&&ch!=‘-‘) ch=getchar();
    if(ch==‘-‘) p=true,ch=getchar();
    while(ch<=‘9‘&&ch>=‘0‘) ans=ans*10+ch-‘0‘,ch=getchar();
    if(p) ans=-ans;
}
void insert(int ,int ,int ,int ,int );
int get_MIN(int ,int ,int );
int main()
{
    int i,j,k,ans2=1000000000;
    map <int ,int > :: iterator iter;
    in(n),in(m);
    tot=n,top=1,flag=false;
    for(i=1;i<=n;i++){
        ctn[i].las=i-1;
        ctn[i].nex=(i+1)%(n+1);
        in(ctn[i].num);
        if(ctn[i].las)
            insert(0,1000000000,1,abs(ctn[i].num-ctn[ctn[i].las].num),1);
        if(MAP.count(ctn[i].num)||flag)
            flag=true;
        else{
            iter=MAP.upper_bound(ctn[i].num);
            if(iter!=MAP.end())
                ans2=min(ans2,iter->first-ctn[i].num);
            if(iter!=MAP.begin())
                iter--,ans2=min(ans2,ctn[i].num-iter->first);
            MAP[ctn[i].num]=i;
        }
        id_now[i]=i;
    }
    for(i=1;i<=m;i++){
        scanf("%s",s);
        if(s[0]==‘I‘){
            in(j),in(k);
            ctn[++tot].num=k;
            ctn[tot].nex=ctn[id_now[j]].nex;
            ctn[ctn[id_now[j]].nex].las=tot;
            ctn[tot].las=id_now[j];
            ctn[id_now[j]].nex=tot;
            ctn[0].nex=ctn[0].las=ctn[0].num=0;
            if(ctn[tot].las)insert(0,1000000000,1,abs(k-ctn[ctn[tot].las].num),1);
            if(ctn[tot].nex)insert(0,1000000000,1,abs(k-ctn[ctn[tot].nex].num),1);
            if(ctn[tot].las&&ctn[tot].nex)insert(0,1000000000,1,abs(ctn[ctn[tot].las].num-ctn[ctn[tot].nex].num),-1);
            if(!flag){
                if(MAP.count(ctn[tot].num)){
                    flag=true,id_now[j]=tot;continue;
                }
                iter=MAP.upper_bound(k);
                if(iter!=MAP.end())ans2=min(ans2,iter->first-k);
                if(iter!=MAP.begin())iter--,ans2=min(ans2,k-iter->first);
                MAP[k]=tot;
            }
            id_now[j]=tot;
        }
        if(s[0]==‘M‘&&s[4]==‘G‘)
            printf("%d\n",get_MIN(0,1000000000,1));
        if(s[0]==‘M‘&&s[4]==‘S‘)
            printf("%d\n",(flag^1)*ans2);
    }
    return 0;
}
void insert(int l,int r,int now,int lim,int x){
    int mid=(l+r)>>1;
    if(l==r){
        data[now].size+=x;
        return ;
    }
    if(lim<=mid){
        if(!data[now].ch[0])data[now].ch[0]=++top;
        insert(l,mid,data[now].ch[0],lim,x);
    }
    else{
        if(!data[now].ch[1])data[now].ch[1]=++top;
        insert(mid+1,r,data[now].ch[1],lim,x);
    }
    data[now].size=data[data[now].ch[0]].size+data[data[now].ch[1]].size;
}
int get_MIN(int l,int r,int now){
    if(l==r)return l;
    int mid=(l+r)>>1;
    if(data[data[now].ch[0]].size)
        return get_MIN(l,mid,data[now].ch[0]);
    else
        return get_MIN(mid+1,r,data[now].ch[1]);
}

原文地址:https://www.cnblogs.com/nietzsche-oier/p/8476111.html

时间: 2024-09-30 11:30:09

bzoj P1058 [ZJOI2007]报表统计——solution的相关文章

[BZOJ 1058][ZJOI2007]报表统计

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1058 题解 方法一:离线+离散化+线段树. 这个方式的常数太大,会T.我在洛谷跑最后两个点TLE了,在BZOJRE了. 具体说一下怎么做吧.首先把所有数离散化,把出现过的绝对值离散化.这样我们就能得到约n+m个数和n+m个绝对值.然后,维护两颗线段树,记录数据是否出现,并记录区间出现的最大值和最小值(因为在线段树上的点是从大到小的,假设我们添加了元素k,然后我们找它的数据排位(下标)),每添

[BZOJ 1058] [ZJOI2007] 报表统计 【平衡树】

题目链接:BZOJ - 1058 题目分析 这道题看似是需要在序列中插入一些数字,但其实询问的内容只与相邻的元素有关. 那么我们只要对每个位置维护两个数 Ai, Bi, Ai 就是初始序列中 i 这个位置的数, Bi 是在 i 这个位置insert的最后一个数. 那么在 i insert一个数 Num 的时候,造成的影响就是使得 Bi 和 A(i+1) 不再相邻,同时使 Bi 与 Num, Num 与 A(i+1) 相邻.然后将 Bi 更新为 Num. 这样就只需要实现一个multiset的功能

bzoj 1058 [ZJOI2007]报表统计(set)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1058 [题意] 一个序列,提供插入,查询相邻最小差值,查询任意最小差值的操作. [思路] Set 用两个set,listed装所有的相邻差值,sorted装所有的数.然后用front[x],last[x]记录位置x上开始和结束的数. 对于Insert,维护listed:删除front[x+1]与last[x]的差值并插入两个新的差值,插入sorted后与前一个后一个作差更新答案. [

【BZOJ 1058】 [ZJOI2007]报表统计

1058: [ZJOI2007]报表统计 Time Limit: 15 Sec  Memory Limit: 162 MB Submit: 2013  Solved: 708 [Submit][Status] Description 小Q的妈妈是一个出纳,经常需要做一些统计报表的工作.今天是妈妈的生日,小Q希望可以帮妈妈分担一些工作,作为她的生日礼物之一.经过仔细观察,小Q发现统计一张报表实际上是维护一个可能为负数的整数数列,并且进行一些查询操作.在最开始的时候,有一个长度为N的整数序列,并且有

BZOJ1058: [ZJOI2007]报表统计

1058: [ZJOI2007]报表统计 Time Limit: 15 Sec  Memory Limit: 162 MBSubmit: 1751  Solved: 614[Submit][Status] Description 小Q的妈妈是一个出纳,经常需要做一些统计报表的工作.今天是妈妈的生日,小Q希望可以帮妈妈分担一些工作,作为她的生日礼物之一.经过仔细观察,小Q发现统计一张报表实际上是维护一个非负整数数列,并且进行一些查询操作.在最开始的时候,有一个长度为N的整数序列,并且有以下三种操作

[补档][ZJOI2007] 报表统计

[ZJOI2007] 报表统计 题目 传送门 小Q的妈妈是一个出纳,经常需要做一些统计报表的工作.今天是妈妈的生日,小Q希望可以帮妈妈分担一些工作,作为她的生日礼物之一. 经过仔细观察,小Q发现统计一张报表实际上是维护一个非负整数数列,并且进行一些查询操作. 在最开始的时候,有一个长度为N的整数序列,并且有以下三种操作: INSERT i k 在原数列的第i个元素后面添加一个新元素k:如果原数列的第i个元素已经添加了若干元素,则添加在这些元素的最后(见下面的例子) MIN_GAP 查询相邻两个元

AC日记——[ZJOI2007]报表统计 bzoj 1058

1058 思路: 平衡树的题: 然而我的平衡树写一次炸一次QwQ: 而且各种tle: 所以stl水过: 代码: #include <set> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define maxn 1000005 class HeadType { private: int head

1058. [ZJOI2007]报表统计【平衡树-splay+堆】

Description 小Q的妈妈是一个出纳,经常需要做一些统计报表的工作.今天是妈妈的生日,小Q希望可以帮妈妈分担一些工 作,作为她的生日礼物之一.经过仔细观察,小Q发现统计一张报表实际上是维护一个可能为负数的整数数列,并 且进行一些查询操作.在最开始的时候,有一个长度为N的整数序列,并且有以下三种操作: INSERT i k 在原数 列的第i个元素后面添加一个新元素k: 如果原数列的第i个元素已经添加了若干元素,则添加在这些元素的最后( 见下面的例子) MIN_GAP 查询相邻两个元素的之间

BZOJ P1059 [ZJOI2007]矩阵游戏——solution

1059: [ZJOI2007]矩阵游戏 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 4604  Solved: 2211[Submit][Status][Discuss] Description 小Q是一个非常聪明的孩子,除了国际象棋,他还很喜欢玩一个电脑益智游戏——矩阵游戏.矩阵游戏在一个N *N黑白方阵进行(如同国际象棋一般,只是颜色是随意的).每次可以对该矩阵进行两种操作:行交换操作:选择 矩阵的任意两行,交换这两行(即交换对应格子的颜