首页 > 题解 > luogu1110 [ZJOI2007]报表统计

luogu1110 [ZJOI2007]报表统计

上午有有人提起了这个题,翻了一下发现还没A。。。

当时写得一个set骗分,bzojAC但是luogu的模拟CCF的老爷机没A。

代码在这

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<set>
#include<map>
#include<queue>
#define inf 1000000000
using namespace std;
int n,m;
int st[500005],ed[500005];
multiset<int> a,b;
map<int,int> mp;
priority_queue<int,vector<int>,greater<int> > q;
inline int read()
{
    int 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;
}
void insert(int x)
{
    mp[x]++;
    if(mp[x]==1)a.insert(x);
}
void push(int x)
{
    int l=*--b.lower_bound(x),r=*b.lower_bound(x);
    q.push(min(x-l,r-x));
    b.insert(x);
}
int main()
{
    n=read();m=read();
    b.insert(inf);b.insert(-inf);
    for(int i=1;i<=n;i++)
    {
        int x=read();
        ed[i]=st[i]=x;
        push(x);
    }
    for(int i=2;i<=n;i++)insert(abs(st[i]-st[i-1]));
    char ch[12];int p,x,t;
    for(int i=1;i<=m;i++)
    {
        scanf("%s",ch);
        if(ch[0]=='I')
        {
            p=read();x=read();
            if(p!=n)
            {
                t=abs(ed[p]-st[p+1]);
                mp[t]--;
                if(!mp[t])a.erase(t);
            }
            insert(abs(ed[p]-x));
            insert(abs(x-st[p+1]));
            ed[p]=x;push(x);
        }
        else if(ch[4]=='S')printf("%d\n",q.top());
        else printf("%d\n",*a.begin());
    }
    return 0;
}

竟然T了!我要卡常!

那就重新想想吧。

题目要求维护一坨数,能够随时插入,查询相邻的数的最小差值,查询任意两数间的最小差值。

第二问比较好写,由于set被卡了,于是写了个treap。。每次插入后查询一下前驱和后继更新一下答案。然后这个答案显然是递减的,所以数列中有相同的数之后就不用理了。

然而第一问就没法用treap做了。。把原数列存在map[]里面。

假设是在原数列第pos个元素后插入一个数x,那么x的右边的那个数就是map[pos+1](如果$pos < n$的话)

x左边的那个数其实就是本来第pos个元素后面插了一坨数的最后一个,用last[i]表示当前(插入x之前)第i个元素后面插进去的一坨数的最后一个数的大小。

所以把x插进去后,原先的差值abs(last[pos]-map[pos+1])不存在了,新增了两个差值为abs(last[pos]-x)和abs(x-map[pos+1])

用优先队列(堆)维护下差值就好了。。。。。

具体姿势:

对于新增的差值abs(last[pos]-x),它是不会被新增的数影响的。。然而abs(x-map[pos+1])可能因为之后在x后面插进去新的数而不存在。。。

判断这个差值存不存在就看x是不是所在元素的最后一次插入的数。每次查询的时候先把队头(小根堆顶)那些不存在的差值弹掉就行了。

事实证明这样写主要时间复杂度是在treap上= =。。。优先队列的速度比treap高明到不知道哪里去了。。treap的速度又比spaly高明到不知道哪里去了。。。

发现大部分的时间都是在treap建树的时候。。。所以直接把数列排序后递归建树。。。(参见文艺平衡树。)

我有特殊的卡常技巧23333

#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int maxn=1000003;
const int inf=0x3f3f3f3f;
struct node
{
    int pos,len,val;
};
int num[maxn],rnd[maxn],l[maxn],r[maxn],last[maxn>>1],map[maxn>>1],len[maxn>>1];
int n,m,minsg,ming,tot,rt,x,a,b,nex,pre;
priority_queue<node>q;
bool operator <(node a,node b){return a.val>b.val;}
int ra,fh;
int inline read()
{
    int 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;
}
char s[11];
inline void outx(int x)
{
    while(x>0||!s[0])s[++s[0]]=x%10,x/=10;
    while(s[0])putchar(s[s[0]--]+48);putchar('\n');
}
inline void lturn(int &x,int R){r[x]=l[R],l[R]=x,x=R;}
inline void rturn(int &x,int L){l[x]=r[L],r[L]=x,x=L;}
void ins(int &x,int val)
{
    if(!x)
    {
        x=++tot,num[x]=val,rnd[x]=rand();
        return;
    }
    if(val<num[x])
    {
        ins(l[x],val);
        if(rnd[l[x]]<rnd[x])
            rturn(x,l[x]);
    }
    else if(val>num[x])
    {
        ins(r[x],val);
        if(rnd[r[x]]<rnd[x])
            lturn(x,r[x]);
    }
    else minsg=0;
}
void getpre(int now,int val)
{
    if(!now)return;
    if(num[now]>val)
        getpre(l[now],val);
    else
        pre=num[now],getpre(r[now],val);
}
void getnex(int now,int val)
{
    if(!now)return;
    if(num[now]<val)
        getnex(r[now],val);
    else
        nex=num[now],getnex(l[now],val);
}
inline void insert(int p,int x)
{
    if(minsg>0)
        pre=-inf,nex=inf,getpre(rt,x),getnex(rt,x),minsg=min(minsg,min(x-pre,nex-x));
    if(minsg>0)
        ins(rt,x);
    ming=min(ming,abs(last[p]-x));
    len[p]++;last[p]=x;
    if(p<n&&ming>0)
        q.push((node){ p,len[p],abs(map[p+1]-x) });
}
void build(int L,int R,int &now,int farnd)
{
    if(L>R)
        return;
    int mid=(L+R+1)>>1;
    now=++tot,num[now]=map[mid],rnd[now]=farnd+23333;
    build(L,mid-1,l[now],rnd[now]);
    build(mid+1,R,r[now],rnd[now]);
}
int main()
{
    n=read();m=read();
    minsg=ming=inf;
    for(int i=1;i<=n;i++)
        last[i]=map[i]=read();
    sort(map+1,map+1+n),rt=(n+1)>>1,build(1,n,rt,0);
    for(int i=1;i<n;i++)
        minsg=min(minsg,map[i+1]-map[i]);
    for(int i=1;i<n;i++)
        len[i]=1,q.push((node){ i,len[i],abs(last[i]-last[i+1]) });
    memcpy(map,last,(n+1)<<2);
    char ch1;
    while(m--)
    {
        for(ch1=getchar();ch1<'A'||ch1>'Z';ch1=getchar());
        if(ch1=='I')
            a=read(),b=read(),insert(a,b);
        else
        {
            ch1=getchar(),ch1=getchar(),ch1=getchar(),ch1=getchar();
            if(ch1=='S')outx(minsg);
            else
            {
                while(q.top().len!=len[q.top().pos])
                    q.pop();
                outx(min(ming,q.top().val));
            }
            while(ch1!='P')
                ch1=getchar();
        }
    }
}