P4647 [IOI2007] sails 船帆 fhq_treap

题意:

戳这里

分析:

题意:\(m\) 个操作每次操作给出 \(h,k\) 在前 \(h\) 个数字中选出 \(k\) 个 +1,使得操作结束后 \(\sum \frac{x\times (x-1)}{2}\) 最小

易证: 最后使得所有数尽可能小最优 ,若 \(x<y\)\(\frac{x\times (x-1)}{2}+\frac{(y+1)\times y}{2}<\frac{x\times (x+1)}{2}+\frac{(y-1)\times y}{2}\)

所以我们将询问离线下来,按 \(h\) 第一关键字,\(k\) 第二关键字升序排序,需要一个数据结构支持每次选出前 \(h\) 中最小的 \(k\) 个,并且区间加 \(1\)

很显然这可以 \(fhq\) ,注意这个题的 \(fhq\) 以数字大小作为键值,由于我们排序,所以不需要考虑位置带来的影响,避免了 \(fhq\) 无法同时维护 序列 和 大小 的缺点

对于每一次查询,直接裂出大小前 \(k\) 小之后,打上加法标记塞回去,就会得到 \(14\) 分的好成绩/kk

我们发现可能存在一种情况,第 \(k\) 小的值没有被取完,即 \([x,k]\)\((k,y]\) 两段区间大小一样,但我们裂开的区间里面只包含了前一段,这样打上加法标记之后合并,\([x,k]\) 这一段就会比 \((k,y]\) 这一段大 \(1\) ,不满足二叉搜索树的性质了,所以我们每次需要将区间裂开成四段 分别是 \([1,x],(x,y-(k-x)](y-(k-x),y],(y,n]\) 我们需要给第 \(1,3\) 段打上加法标记之后塞回去,所以我们发现我们的 \(fhq\) 需要支持按 \(siz\) 分裂 (取出大小最小的前\(k\) 个)还需要支持按 \(val\) 分裂,(取出大/小于 第 \(k\) 小的区间)

我直接裂开,第一次看见需要两种 \(split\)\(fhq\)

代码:

#include<bits/stdc++.h>
#define mk(x,y) make_pair(x,y)
#define fir first
#define sec second
#define pii pair<int,int>
#define lc son[rt][0]
#define rc son[rt][1]
using namespace std;

namespace zzc
{
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
        return x*f;
    }

    const int maxn = 1e5+5;
    int n;
    long long ans;
    pii q[maxn];

    namespace fhq_treap
    {
        int val[maxn],son[maxn][2],rnd[maxn],siz[maxn],tag[maxn];
        int cnt,rt;

        int new_node()
        {
            int u=++cnt;
            siz[u]=1;val[u]=0;tag[u]=0;
            rnd[u]=rand();
            return u;
        }
        
        void pushup(int rt)
        {
            siz[rt]=siz[lc]+siz[rc]+1;
        }

        void pushdown(int rt)
        {
            if(tag[rt])
            {
                tag[lc]+=tag[rt];
                tag[rc]+=tag[rt];
                val[lc]+=tag[rt];
                val[rc]+=tag[rt];
                tag[rt]=0;
            }
        }

        int merge(int x,int y)
        {
            if(!x||!y) return x|y;
            pushdown(x);pushdown(y);
            if(rnd[x]<rnd[y])
            {
                son[x][1]=merge(son[x][1],y);
                pushup(x);
                return x;
            }
            else
            {
                son[y][0]=merge(x,son[y][0]);
                pushup(y);
                return y;
            }
        }

        void split1(int tmp,int k,int &u,int &v)
        {
            if(!tmp)
            {
                u=0;v=0;
                return ;
            }
            pushdown(tmp);
            if(siz[son[tmp][0]]<k)
            {
                u=tmp;
                split1(son[u][1],k-siz[son[tmp][0]]-1,son[u][1],v);
                pushup(u);
            }
            else
            {
                v=tmp;
                split1(son[v][0],k,u,son[v][0]);
                pushup(v);
            }
        }
        
        void split2(int tmp,int k,int &u,int &v)
        {
        	if(!tmp)
        	{
        		u=0;v=0;
        		return ;
			}
			pushdown(tmp);
			if(val[tmp]<=k)
			{
				u=tmp;
				split2(son[u][1],k,son[u][1],v);
				pushup(u);
			}
			else
			{
				v=tmp;
				split2(son[v][0],k,u,son[v][0]);
				pushup(v);
			}
		}
        
        void update(int k)
        {
            int x,y,z,w,tmp,num1,num2;
            split1(rt,k,x,y);
            tmp=x;
			while(son[tmp][1]) tmp=son[tmp][1];
			tmp=val[tmp];
            rt=merge(x,y);
            
            split2(rt,tmp-1,x,y);
            num1=siz[x];
			rt=merge(x,y);
			
			split2(rt,tmp,x,y);
            num2=siz[x];
            rt=merge(x,y);
            
            split1(rt,num1,x,y);
            split1(y,num2-k,y,z);
            split1(z,k-num1,z,w);
            val[z]+=1;
            val[x]+=1;
            tag[z]+=1;
            tag[x]+=1;
            rt=merge(merge(merge(x,y),z),w);
        }

        void calc()
        {
            int x=rt,y,z;
            for(int i=1;i<=cnt;i++)
            {
                split1(x,1,y,z);
                ans+=1ll*val[y]*(val[y]-1)/2;
                x=z;
            }
        }
		
		void insert(int k)
		{
			if(!k) return ;
			for(int i=1;i<=k;i++) rt=merge(new_node(),rt);
		}
		
    }
    using namespace fhq_treap;

    void work()
    {
        srand(time(0));
        int x,y;
        n=read();
        for(int i=1;i<=n;i++)
        {
            x=read();y=read();
            q[i]=mk(x,y);
        }
        sort(q+1,q+n+1);
        int tmp=0;
        for(int i=1;i<=n;i++)
        {
            insert(q[i].fir-tmp);
            update(q[i].sec);
            tmp=q[i].fir;
        }
        calc();
        printf("%lld\n",ans);
    }


}

int main()
{
    zzc::work();
    return 0;
}
posted @ 2021-01-05 15:40  youth518  阅读(68)  评论(0编辑  收藏  举报