1. 程式人生 > >NOIP2017 列隊 題解報告【56行線段樹】

NOIP2017 列隊 題解報告【56行線段樹】

題目描述

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

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

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

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

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

(x,y)(1xn,1ym)描述,表示第 xx 行第 yy 列的學生離隊。

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

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

  2. 向前看齊。這時第一行保持不動,所有學生向前填補空缺。不難發現在這條 指令之後,空位在第 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: 複製
2 2 3 
1 1 
2 2 
1 2 
輸出樣例#1: 複製
1
1
4

說明

【輸入輸出樣例 1 說明】


列隊的過程如上圖所示,每一行描述了一個事件。 在第一個事件中,編號為 1 的同學離隊,這時空位在第一行第一列。接著所有同學 向左標齊,這時編號為 2 的同學向左移動一步,空位移動到第一行第二列。然後所有同 學向上標齊,這時編號為 4 的同學向上一步,這時空位移動到第二行第二列。最後編號 為 1 的同學返回填補到空位中。

【資料規模與約定】


資料保證每一個事件滿足 1 \le x \le n,1 \le y \le m1xn,1ym

題解

NOIP2017最後一道題,考試40min時就寫到了,可以說心情澎湃。。【然而並沒有想到正解】

看到部分分很多,考場拿了部分分

前50分:

前50分q很小,用O(q^2)倒推就好了

把之前的出隊位置記下來,若當前詢問位置為(x,y),上一次出隊位置為(x1,y1)

若(x,y) == (n,m),說明是剛入隊,那麼就轉移到上次出隊位置(x1,y1)

假若y == m且x1 <= x,說明在上次一隊形調整中(x,y)是由(x,y + 1)移動過來的,那麼y++

假若x == n且y1 <= y,同理可以退出上一次在(x + 1,y),那麼x++

最後就會推回原來的位置

30分x=1

X=1就是一條鏈,我們每次需要從區間選出第k大放到末尾,你能想到什麼? splay?是的。我考場寫了splay,親測10分【可能是我沒寫好,聽說如果寫split的splay可以A】 求區間第K大可以用線段樹,以值為區間建樹,區間和表示這段區間的數使用了多少次,然後就從根節點開始, (左區間大小 - 左區間使用次數) = 左區間仍存在的個數x,若x >= k,往左子樹找,否則往右子樹找,最終到達 的葉子就是我們要找的第k大的值 後面放進來的值我們可以看做是m + 1,m + 2,先存到一個vector表裡面,假若我們求出的k大值超出了m, 就減去m從表中直接找出

100分

其實30分演算法提示的已經很明顯了,或者說已經可以當正解了 正解中,我們只需對每一行建一棵線段樹,對最後一列單獨建一棵線段樹 同樣也開相同數量的vector表一起維護就好了 線段樹會爆空間怎麼辦?我們發現所有線段樹的初始值都是確定的0,可以考慮動態開節點。 因為沒有訪問過,每個新節點的值自然為0,非常方便 具體操作: (x,y)出隊,我們找到第x棵線段樹,找到第y個未使用的值【同30分演算法】,再從最後一列的 線段樹中找到第x個值加入第x個領接表【加入第x行末尾】,最後將找出的值加入最後一列末尾 複雜度分析: 由於最多操作q次,所以新加入的節點不會超過3*10^5,領接表空間不會爆 時間複雜度O(qlogn) 56行程式碼【壓行不算太嚴重。。】
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
using namespace std;
const int maxn = 600005,maxm = 20000005,INF = 1000000000;
inline int read(){
	int out = 0,flag = 1;char c = getchar();
	while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
	while (c >= 48 && c <= 57) {out = out * 10 + c - 48; c = getchar();}
	return out * flag;
}
int n,m,q,Max;
vector<LL> G[maxn];
struct node{int sum,ls,rs; node() {ls = rs = 0;} }e[maxm];
int siz = 0,rt[maxn],pos;
void modify(int& u,int l,int r){
	if (!u) u = ++siz; e[u].sum++;
	if(l < r) {
		int mid = l + r >> 1;
		if (mid >= pos) modify(e[u].ls,l,mid);
		else modify(e[u].rs,mid + 1,r);
	}
}
int Query(int u,int l,int r,int k){
	if (l == r) return l;
	int mid = l + r >> 1,sizl = mid - l + 1 - e[e[u].ls].sum;
	if (sizl >= k) return Query(e[u].ls,l,mid,k);
	else return Query(e[u].rs,mid + 1,r,k - sizl);
}
LL Del_r(int x,LL v){
	pos = Query(rt[n + 1],1,Max,x); modify(rt[n + 1],1,Max);
	LL ans = pos <= n ? 1ll * pos * m: G[n + 1][pos - n - 1];
	G[n + 1].push_back(v ? v : ans);
	return ans;
}
LL Del_l(int x,int y){
	pos = Query(rt[x],1,Max,y); modify(rt[x],1,Max);
	LL ans = pos < m ? 1ll * (x - 1) * m + pos : G[x][pos - m];
	G[x].push_back(Del_r(x,ans));
	return ans;
}
int main()
{
	int x,y;
	n = read(); m = read(); q = read(); Max = max(m,n) + q;
	while (q--){
		x = read(); y = read();
		if (y == m) printf("%lld\n",Del_r(x,0));
		else printf("%lld\n",Del_l(x,y));
	}
	return 0;
}


相關推薦

NOIP2017 列隊 題解報告56線段

題目描述 Sylvia 是一個熱愛學習的女♂孩子。 前段時間,Sylvia 參加了學校的軍訓。眾所周知,軍訓的時候需要站方陣。 Sylvia 所在的方陣中有n \times mn×m名學生,方陣的行數為 nn,列數為 mm。 為了便於管理,教官在訓練開始時,按照

HDOJ5692解題報告dfs序+線段

註意 表示 pre fine efi tree using push cnblogs 題目地址:   http://acm.hdu.edu.cn/showproblem.php?pid=5692 題目概述:   中文題面就不贅述了。 大致思路:   這個題給出的是一棵樹,我

NOIP2016天天愛跑步 題解報告lca+樹上統計(桶)

題目描述 小c同學認為跑步非常有趣,於是決定製作一款叫做《天天愛跑步》的遊戲。«天天愛跑步»是一個養成類遊戲,需要玩家每天按時上線,完成打卡任務。 這個遊戲的地圖可以看作一一棵包含 nn個結點和 n-1n−1條邊的樹, 每條邊連線兩個結點,且任意兩個結點存在一條

BZOJ1513 [POI2006]Tet-Tetris 3D 二維線段

get mem HR void 修改 mod 3D 理解 fine 題目鏈接 BZOJ1513 題解 真正地理解了一波線段樹標記永久化的姿勢 每個節點維護兩個值\(v\)和\(tag\) \(v\)代表兒子中的最值 \(tag\)代表未下傳的最值 顯然節點的區間大於等於\(

Luogu P1637 三元上升子序列權值線段By cellur925

題目傳送門 emmm..不開結構體的線段樹真香! 首先我們知道“三元上升子序列”的個數就是對於序列中的每個數,**它左邊比他小的數*它右邊比他大的數**。但是如何快速求出這兩個數? 我們用到權值線段樹來維護。一般我們的線段樹都是以下標延伸的,但是這裡我們用的是權值,一般需要離散化,效果相當於一個桶。

Check Corners HDU - 2888二維線段

題目連結   很多人寫這道題都用的是二維RMQ,但是,我覺得這道題可以鍛鍊一下我二維線段樹的思維,但是,無獨有偶,這道題會卡一些二維線段樹的模板,一開始我想也沒想,直接敲了剛學的線段樹,然後不停的RE,後來改了下,換成單點更新與區間更新二維線段樹,還是不行TLE了,於是,就開始想,

Mosaic HDU - 4819二維線段

題目連結   這道題難就只是難在題目難讀,題意讀懂後就是一道普通的二維線段樹更新查詢問題。   題意:給你一個N*N的矩陣,並且已經建立了初始值,然後給你個點以及L,很多人不解其義,其實就是給你個點,然後查的是以(x, y)為基礎的點,在以左上角(x-L/2, y-L

Matrix Searching ZOJ - 2859二維線段

題目連結   簡單的二維線段樹求區間最小值問題,還不用修改操作。 #include <iostream> #include <cstdio> #include <cmath> #include <string> #includ

Census UVA - 11297二維線段

題面 This year, there have been many problems with population calculations, since in some cities, there are many emigrants, or the population growth i

Get Many Persimmon Trees POJ - 2029二維線段

題目連結【簡單題】   一道簡單的二維線段樹,查詢的是一個規定長寬的長方形內的最多點數,因為W、H都比較的小,所以不妨可以直接上暴力二維線段樹查詢即可。 #include <iostream> #include <cstdio> #include &

Luck and Love HDU - 1823二維線段

題目連結   最近敲了些二維線段樹,這也是我題單裡的一道題,無非多出來的操作就是對區間端點的處理問題,給出的是浮點數,不如就把他向取整咯,一開始取整的時候總是會遇到些問題就是總是取不到恰好,會少位,怎麼辦呢?於是我這裡學到了個騷操作:(int)(x*10. + efs);其中,ef

Mobile phones POJ - 1195二維線段

題目連結   關於這道題,我用了二維線段樹來做的,但是,我這裡又一個疑問,就是我用了個四叉樹的線段樹的程式碼卻是始終過不了一直在WA,若恰好有大佬經過,能幫小生看一下我不成器的程式碼嗎?   先放上討論哪裡錯的程式碼供大家討論,幫我修改,謝謝! #includ

bzoj 5294: [Bjoi2018]二進位制動態dp+線段

不太清楚是不是動態dp……? 這個維護其實和最大連續子段差不多,維護l[x][y],r[x][y],m[x][y]分別表示包含左兒子的01個數為(x,y)的區間個數,包含右兒子的01個數為(x,y)的區間個數,和01個數為(x,y)的所有區間個數 x表示1的個數情況,0表示0個,1表示1個,2表示>=2

[bzoj4504]K個串可持久化線段

【題目連結】    【題解】   首先記下每個點向右所控制的區間,就是它到下一個與它相同的位置-1。   我們考慮對於每個左端點維護一棵線段樹下標表示以該點為右端點的區間的答案。   那麼左端點為1的區間可以O(N)O(N)暴力求出。   對於兩個相

2019雅禮集訓可持久化線段模型轉化D1T2Permutation

目錄 題意 輸入格式 輸出格式 思路 程式碼 題意 給定一個長度為n的序列A[],你需要確定一個長度為n的排列P[],定義當前排列的值為: \[\sum_{i=1}^{n}{A[i]P[i]}\] 現在給定一個整數k,需要你求出,在所有可能的排列中(顯然有n!種),最小的k個"

可持久化線段2019雅禮集訓 permutation

題目: 分析: 比較噁心的一道題。 首先,將式子轉化一下,變成 ∑ A

[JSOI2009]等差數列差分+線段

作為水到一等的弱省OI蒟蒻,甚至以為自己能聽懂SDWC的省選班。去了以後發現除了搜尋、仰望大佬和看妹子(還真有妹子很不錯)以外啥都不會…每天都是聽一小時之後棄療。 然而唯一有基礎的線段樹…也是一上來“大家都會寫線段樹吧”然後丟一道黑題(也就是這道)。同伴在身邊

BZOJ 1513Tet-Tetris 3D二維線段

Description 在新遊戲中你將知道落下的立方體資訊以及位置,你的任務就是回答所有立方體落下後最高的方塊的高度.所有的立方體在下落過程中都是垂直的並且不會旋轉.平板左下角座標為原點,並且平行於座

bzoj3306dfs序+線段

  對於 100% 的資料:n, Q ≤ 10^5。 題解:首先按dfs序建線段樹。            對於換根操作。我們分三種情況討論(設root為當前根,x為要查詢的子樹)            1.若root==x直接查詢整棵樹即可。            2.若lca(root,x)!=x那

hiho一下 第124周 #1421 : 四叉 二維線段

小Ho:樸素的想法是我用一個二維陣列來把整個平面圖表示出來。假設座標的範圍是L,那麼就需要一個L*L的陣列。 對於(a,b)和r,我就檢查a-r到a+r行的b-r列到b+r列,看其中是否存在有點,並且點到(a,b)的距離是小於等於r的。 對於L超過10000的情況就沒有辦法實現了。 小Hi:沒錯,在座標範圍