1. 程式人生 > 實用技巧 >習題:列隊(主席樹)

習題:列隊(主席樹)

題目

傳送門

思路

考慮如果有一群人直往右跑,那麼設$aim_i $為最優策略下其的終止位置,設$p_i$為他們現在的位置,按照題目給定的式子,消耗的體力值總和即為$\sum_^|aim_i-p_i|$

因為對於所有的人都有$aim_i>p_i$,所以原式即為$\sum_aim_i-\sum_p_i$,也就是指他們的互相的位置其實對答案沒有什麼影響,貪心地去考慮,一定有一個分界點,其左邊的所有人向右跑且不會超過分界點,右邊的所有人向左跑,且不會超過分界點。那麼考慮一顆主席樹,方便裂出$l\sim r$中的所有點,這裡的主席樹是按照權值來的,所以可以在主席樹上進行二分,把求出分界點的時間從$log_nlog_n$變成$log_n$,值域範圍需要開兩倍

程式碼

#pragma GCC optimize(2)
#include<iostream>
#include<cstdio>
using namespace std;
#define pii pair<long long,int>
#define x first
#define y second
namespace IO
{
    void read(int &x)
    {
        x=0;
        int f=1;
        char c=getchar();
        while('0'>c||c>'9')
        {
            if(c=='-')
                f=-1;
            c=getchar();
        }
        while('0'<=c&&c<='9')
        {
            x=(x<<3)+(x<<1)+c-'0';
            c=getchar();
        }
        x*=f;
    }
    void write(long long x)
    {
        if(x>9)
            write(x/10);
        putchar(x%10+'0');
    }
}
namespace lst
{
    inline pii operator + (const pii &a,const pii &b)
    {
        return make_pair(a.x+b.x,a.y+b.y);
    }
    int cnt;
	struct node
	{
		int l,r;
		int lson,rson;
		int siz;
		long long val;
	}tre[1000000*30+5];
	void build(int l,int r,int &k)
	{
        k=++cnt;
        tre[k].l=l;
        tre[k].r=r;
        if(l==r)
            return;
        int mid=(l+r)>>1;
        build(l,mid,tre[k].lson);
        build(mid+1,r,tre[k].rson);
        tre[k].siz=tre[tre[k].lson].siz+tre[tre[k].rson].siz;
        tre[k].val=tre[tre[k].lson].val+tre[tre[k].rson].val;
    }
    void change(int las,int &now,int val,int pos)
    {
        if(tre[las].l>pos||pos>tre[las].r)
            return;
        now=++cnt;
        tre[now]=tre[las];
        if(tre[now].l==tre[now].r)
        {
            tre[now].val+=val;
            tre[now].siz++;
            return;
        }
        change(tre[las].lson,tre[now].lson,val,pos);
        change(tre[las].rson,tre[now].rson,val,pos);
        tre[now].siz=tre[tre[now].lson].siz+tre[tre[now].rson].siz;
        tre[now].val=tre[tre[now].lson].val+tre[tre[now].rson].val;
    }
    pii ask(int las,int now,int l,int r)
    {
        if(tre[now].l>r||l>tre[now].r)
            return make_pair(0,0);
        if(l<=tre[now].l&&tre[now].r<=r)
            return make_pair(tre[now].val-tre[las].val,tre[now].siz-tre[las].siz);
        return ask(tre[las].lson,tre[now].lson,l,r)+ask(tre[las].rson,tre[now].rson,l,r);
    }
    long long calc(int las,int now,int k,int add,int dis)
    {
        int siz=tre[tre[now].lson].siz-tre[tre[las].lson].siz+add;
       	int mid=tre[tre[now].lson].r;
        if(mid<k)
       		return calc(tre[las].rson,tre[now].rson,k,siz,dis);
       	if(mid>k+dis)
       		return calc(tre[las].lson,tre[now].lson,k,add,dis);
        if(siz==mid-k)
            return mid;
        if(siz>mid-k)
            return calc(tre[las].rson,tre[now].rson,k,siz,dis);
        else
            return calc(tre[las].lson,tre[now].lson,k,add,dis);
    }   
}
using namespace lst;
using namespace IO;
int n,m;
int rt[500005];
int l,r,k;
long long s[2000005];
int main()
{
    read(n);
    read(m);
    build(1,2000000,rt[0]);
    for(int i=1;i<=2000000;i++)
        s[i]=s[i-1]+i;
    for(int i=1,val;i<=n;i++)
    {
        read(val);
        change(rt[i-1],rt[i],val,val);
    }
    for(int i=1;i<=m;i++)
    {
        read(l);
        read(r);
        read(k);
        int d=calc(rt[l-1],rt[r],k-1,0,r-l+1);
        pii t1=ask(rt[l-1],rt[r],1,d);
        pii t2=ask(rt[l-1],rt[r],d+1,2000000);
        write(((s[d]-s[k-1])-t1.x)+(t2.x-(s[k+r-l]-s[d])));   
        putchar('\n');
    }
    return 0;	
}