1. 程式人生 > >洛谷P3960 列隊【noip2017D2T3】(多顆線段樹求第k大)

洛谷P3960 列隊【noip2017D2T3】(多顆線段樹求第k大)

題目描述

Sylvia 是一個熱愛學習的女♂孩子。

前段時間,Sylvia 參加了學校的軍訓。眾所周知,軍訓的時候需要站方陣。

Sylvia 所在的方陣中有n \times mn×m 名學生,方陣的行數為 nn ,列數為 mm 。

為了便於管理,教官在訓練開始時,按照從前到後,從左到右的順序給方陣中 的學生從 1 到 n \times mn×m 編上了號碼(參見後面的樣例)。即:初始時,第 ii 行第 jj 列 的學生的編號是(i-1)\times m + j(i−1)×m+j 。

然而在練習方陣的時候,經常會有學生因為各種各樣的事情需要離隊。在一天 中,一共發生了 q q 件這樣的離隊事件。每一次離隊事件可以用數對(x,y) (1 \le x \le n, 1 \le y \le m)(x,y)(1≤x≤n,1≤y≤m) 描述,表示第 xx 行第 yy 列的學生離隊。

在有學生離隊後,隊伍中出現了一個空位。為了隊伍的整齊,教官會依次下達 這樣的兩條指令:

向左看齊。這時第一列保持不動,所有學生向左填補空缺。不難發現在這條 指令之後,空位在第 xx 行第 mm 列。

向前看齊。這時第一行保持不動,所有學生向前填補空缺。不難發現在這條 指令之後,空位在第 nn 行第 mm 列。
教官規定不能有兩個或更多學生同時離隊。即在前一個離隊的學生歸隊之後, 下一個學生才能離隊。因此在每一個離隊的學生要歸隊時,隊伍中有且僅有第 nn 行 第 mm 列一個空位,這時這個學生會自然地填補到這個位置。

因為站方陣真的很無聊,所以 Sylvia 想要計算每一次離隊事件中,離隊的同學 的編號是多少。

注意:每一個同學的編號不會隨著離隊事件的發生而改變,在發生離隊事件後 方陣中同學的編號可能是亂序的。

輸入輸出格式

輸入格式:

輸入共 q+1q+1 行。

第 1 行包含 3 個用空格分隔的正整數 n, m, qn,m,q ,表示方陣大小是 nn 行 mm 列,一共發 生了 qq 次事件。

接下來 qq 行按照事件發生順序描述了 qq 件事件。每一行是兩個整數 x, yx,y ,用一個空 格分隔,表示這個離隊事件中離隊的學生當時排在第 xx 行第 yy 列。

輸出格式:

按照事件輸入的順序,每一個事件輸出一行一個整數,表示這個離隊事件中離隊學 生的編號。

————————————————————————————————————

題解

1.
前n棵是維護每一行的第1列到第m-1列的
第n+1棵是維護第m列的。
關鍵在於:300000*300000咋整?
只能用動態開點來解決。
2.
y!=m時
把第x行第y個的值找到記為ans並刪除
把第m列第x個找到並刪除,插入第x行最後一個
把ans插入第m列最後一個那裡
y==m直接在x=m那一列操作即可

注意

temp2判斷是不是原始佇列的人(包括初始狀態和被迫移動的人)
如果是,就可以用目測(正方形內求每一個位置的座標)法來獲得編號,所以初始狀態下c的值不需要
至於主動離隊的人,就需要維護c值
離開的人的位置實際上不從線段樹中去掉,只是標記為沒人(size=1),用處就是判斷第k大時去掉這些位置,
閱讀程式碼時容易把l所在的位置當做實際長(或寬),其實去掉size找到的第k大才是當下實際所在座標(x,y)

程式碼

#include<iostream>
#include<cstring> 
#include<cstdio>
#include<cstdlib> 
using namespace std;
#define ll long long
struct position
{
    int x,y;
}a[310000];
struct segmenttree
{
    int lc,rc;ll c;
}seg[6100000];
int tail[310000],root[310000];
int tot=0,size[6100000];
void insert(int &now,int l,int r,int k,ll num)
{
    if(now==0) now=++tot;
    if(l==r)
    {
        seg[now].c=num;
        size[now]=0;
        return ;
    }
    int mid=(l+r)>>1;
    if(k<=mid) insert(seg[now].lc,l,mid,k,num);
    else       insert(seg[now].rc,mid+1,r,k,num);
    size[now]=size[seg[now].lc]+size[seg[now].rc];
}
ll findkth(int &now,int l,int r,int k,int &ans)
{
    if(now==0) now=++tot;
    if(l==r)
    {
        ans=l;
        ll temp=seg[now].c;seg[now].c=0;
        size[now]=1;
        return temp;
    }
    int mid=(l+r)>>1;ll temp;
    if(mid-l+1-size[seg[now].lc]>=k)
    temp=findkth(seg[now].lc,l,mid,k,ans);
    else 
    temp=findkth(seg[now].rc,mid+1,r,k-(mid-l+1-size[seg[now].lc]),ans);
    size[now]=size[seg[now].lc]+size[seg[now].rc];
    return temp;
}
int main()
{
    ll n,m,q;
    scanf("%lld%lld%lld",&n,&m,&q);
    for(int i=1;i<=n;i++)tail[i]=m-1;
    tail[n+1]=n;
    for(int i=1;i<=q;i++)
    {
        scanf("%d%d",&a[i].x,&a[i].y);
        if(a[i].y==m)
        {
            int temp2;
            ll temp=findkth(root[n+1],1,n+q,a[i].x,temp2);
            if(temp2<=n)temp=temp2*m;
            printf("%lld\n",temp);
            insert(root[n+1],1,n+q,++tail[n+1],temp);
        }
        else 
        {
            int temp2;
            ll temp=findkth(root[a[i].x],1,m+q,a[i].y,temp2);
            if(temp2<m)temp=(a[i].x-1)*m+temp2;
            printf("%lld\n",temp);
            insert(root[n+1],1,n+q,++tail[n+1],temp);
            temp=findkth(root[n+1],1,n+q,a[i].x,temp2);
            if(temp2<=n)temp=temp2*m;
            insert(root[a[i].x],1,m+q,++tail[a[i].x],temp);
        }
    }
//system("pause");
return 0;
}