1. 程式人生 > >LOJ535「LibreOJ Round #6」花火-題解

LOJ535「LibreOJ Round #6」花火-題解

記得開long long,空間要兩倍!

  • 題目簡述

給你一個nn的全排列,相鄰的之間可以任意交換,只有一次可以交換不相鄰的位置的數的機會,求出能讓其排好序的最少交換次數。

分析:

如果沒有那一次不相鄰的交換機會,那麼就是一個求逆序對個數的裸題了。

那麼有了這個之後,我們暴力的想法是列舉n2n^2的交換情況,每次再nlognnlogn計算逆序對個數,總複雜度為n3lognn^3logn,但是顯然過不了。

所以我們考慮,對於一個點,我們可以看作這樣的一個點對(i,hi)(i,h_i),表示下標為ii,高度為hih_i,那麼我們交換的點必須要滿足:i

<j,hi>hji<j,h_i>h_j,否則會增加逆序對的個數。

而我們將其放在平面上來看,會是這樣的:

eg

紅色的點為(i,hi)(i,h_i),綠色的點為(j,hj)(j,h_j),那麼如果交換這兩個點,我們可以發現,減少的逆序對個數為1+矩形內的點的個數1+\text{矩形內的點的個數},因為你原來矩形內的每個點和點jj會產生逆序對,然後矩形內的點和點ii產生逆序對,最後點i,ji,j的逆序對,所以交換後就會減少這麼多。

那麼我們只需找到包含點最多的矩形,然後將其左上角的點和右下角的點交換之後答案就為:

ans=tot矩形內的點的個數×21+1ans=tot-\text{矩形內的點的個數}\times 2 -1+1

tottot為總的逆序對個數,後面1+1-1+1是因為開始i,ji,j會減少一個,但是你交換i,ji,j又會增加一步操作,所以是這樣。

我們暴力找的話還是n2n^2的,但是我們可以發現,對於一個點(k,hk)(k,h_k),如果k<i,hk>hik<i,h_k>h_i,那麼用點k,jk,j肯定比點i

,ji,j要優秀,因為這兩個矩形是包含的關係,如下圖:

eg

圖上點CC為點kkAA為點iiBB為點jj

同理,對於一個點(w,hw)(w,h_w),如果w>j,hw<hjw>j,h_w<h_j,那麼i,wi,w肯定比i,ji,j要優秀,如下圖:

eg

AA為點ii,點CC為點jj,點EE為點ww

所以根據這個單調性,我們可以用整體二分+主席樹找到包含點最多的矩形,具體細節參考下面這個部落格【Orz-IN

但是,這個方法是O(nlog2n)O(nlog^2n)的,所以雖然能過,但是常數巨大且不好寫。

不妨反過來思考。 我們可以對於每一個點,找出包含它的最大的矩形。 那麼我們對於點(i,hi)(i,h_i),我們前面找最小的ll,且滿足l<i,hl<hil<i,h_l<h_i,找後面最大的rr,且滿足r>i,hr<hir>i,h_r<h_i,這個就是能包含它的最大的矩形了,所以對於所有點(j,hj)(j,h_j),只要滿足ljr,hrhjhll\leq j\leq r,h_r\leq h_j\leq h_l,那麼這個點就會對其作出貢獻。

我們對於矩形不好處理,所以將其轉化為點對,一個點(x,y)(x,y)表示左上角的下標在xx右下角的下標在yy的矩形,那麼我們每次只用在(li1,i+1r)(l\sim i-1,i+1\sim r)內的點加11即可。

所以我們用掃描線的思想將一個矩形拆成兩條線,一條入線,一條出線。 我們按照新的平面的yy座標從下往上掃,所以我們將原來一個(i,hi)(i,h_i)點表示的矩形拆為(li1,i+1)(l\sim i-1,i+1)的一條線和(li1,r+1)(l\sim i-1,r+1)的兩條線,當我們遇到第一條線時表示進入了這個矩形,就將li1l\sim i-111,遇到第二條線就表示已經出了這個矩形,我們要將這個矩形的影響消除,就將li1l\sim i-111,然後我們每次取當前這條線上的最大值更新最多點矩陣的點數即可,這個可以用一個線段樹維護。

所以先預處理,用歸併排序或者樹狀陣列的方式求出總的逆序對的個數tottot,記最多的點數為pp,答案就為: tot2×ptot-2\times p

所以在nlognnlogn的預處理,nlognnlogn的求ppO(1)O(1)回答答案即可。 總複雜度O(nlogn)O(nlogn),比分治的O(nlog2n)O(nlog^2n)要優秀的多。

程式碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int M=3e5+10;
int n,H[M];
int lmax[M],rmin[M];
struct node{
	int l,r,type,ck;
	node(){}
	node(int a,int b,int c,int d):l(a),r(b),type(c),ck(d){}
	bool operator <(const node &a)const{return ck<a.ck||(ck==a.ck&&type>a.type);}
}line[M<<1];//因為每個矩形拆成兩條線,所以空間開兩倍
int tot;
int maxv[M<<2],lazy[M<<2];
void pushup(int o){
	maxv[o]=max(maxv[o<<1],maxv[o<<1|1]);
}
void pushdown(int o){
	if(!lazy[o]) return;
	maxv[o<<1]+=lazy[o];
	maxv[o<<1|1]+=lazy[o];
	lazy[o<<1]+=lazy[o];
	lazy[o<<1|1]+=lazy[o];
	lazy[o]=0;
}
void update(int o,int l,int r,int L,int R,int v){
	if(L<=l&&r<=R){
		maxv[o]+=v;lazy[o]+=v;
		return;
	}
	int mid=l+r>>1;
	pushdown(o);
	if(L<=mid) update(o<<1,l,mid,L,R,v);
	if(R>mid) update(o<<1|1,mid+1,r,L,R,v);
	pushup(o);
}
//線段樹區間加維護最大值
#define lowbit(a) ((a)&(-(a)))
ll bit[M];
void add(int a,ll b){
	for(;a<=n;a+=lowbit(a))bit[a]+=b;
}
ll query(int a){
 	int ans=0;
	for(;a;a-=lowbit(a))ans+=bit[a];
	return ans;	
}
//樹狀陣列求逆序對個數
ll ans;//記得開long long
void init(){
	for(int i=n;i>=1;i--){
		ans+=1ll*query(H[i])
            
           

相關推薦

LOJ535LibreOJ Round #6-題解

記得開long long,空間要兩倍! 題目簡述 給你一個nnn的全排列,相鄰的之間可以任意交換,只有一次可以交換不相鄰的位置的數的機會,求出能讓其排好序的最少交換次數。 分析: 如果沒有那一次不相鄰的交換機會,那麼就是一個求逆序對個數的裸題了。 那麼

[決策單調 分治] LOJ#535. LibreOJ Round #6

如果 i<ji<j 且 ai>ajai>aj 那麼 ii 作為左端點比 jj 優,右端點同理 那麼搞出兩個上升序列,發現右端點遞增的時候左端點也是單調上升的,也就是gjghfd和vector說的具有決策單調 分治就好了 #inc

loj516 LibreOJ β Round #2DP 一般看規律

clear pac ret stdio.h eoj 技術 sort logs targe 傳送門:https://loj.ac/problem/516 【題解】 那段代碼求的是相同的數中間隔最小的值。 離散後用set維護每個值出現次數,每次操作相當於合並兩個set,這步可以

LibreOJ #514. LibreOJ β Round #2模擬只會猜題意

clas oid loj gist mem bsp num clu n) 二次聯通門 : LibreOJ #514. 「LibreOJ β Round #2」模擬只會猜題意 /* LibreOJ #514. 「LibreOJ β Round #2

LibreOJ #525. LibreOJ β Round #4多項式

cnblogs for getchar 多項式 tps cst row style clu 二次聯通門 : LibreOJ #525. 「LibreOJ β Round #4」多項式 官方題解 : /* LibreOJ

LibreOJ #515. LibreOJ β Round #2貪心只能過樣例

tdi thml http column printf name target sum pro 題目描述 一共有 nnn個數,第 iii 個數 xix_ix?i?? 可以取 [ai,bi][a_i , b_i][a?i??,b?i??] 中任意值。設

Loj515 LibreOJ β Round #2貪心只能過樣例 - Bitset,Dp

pac namespace als out name c++ 可行性 bsp mes bitset的基本應用了 類似可行性背包的dp考慮 復雜度O(nmL/64) 1 #include <bits/stdc++.h> 2 using namespace s

loj548 LibreOJ β Round #7某少女附中的體育課

dft col aps == ret hid 要求 但是 freopen 這道題好神啊!!! 發現這題就是定義了一種新的卷積,然後做k+1次卷積。 這裏我們就考慮構造一個變換T,使得$T(a) \cdot T(b) =T(a°b)$,這裏是讓向量右乘這個轉移矩陣。 於

LOJ #559. LibreOJ Round #9ZQC 的迷宮

AS left str int 交互 stdout block tro flush 一道ZZ結論題,主要是來寫一寫交互題的。 我們要先知道一句話: 扶著墻是肯定可以走出簡單迷宮的。 然後我們冷靜分析問題。若這個迷宮是\(n\times m\)的,那麽最多有\(2mn+n

LOJ565. LibreOJ Round #10mathematican 的二進位制(NTT)

題目連結 https://loj.ac/problem/565 題解 首先,若進行所有操作之後成功執行的運算元為 \(m\),最終得到的數為 \(w\),那麼發生改變的二進位制位的數量之和(即代價之和)為 \(2m - {\rm bit}(w)\)。其中,\({\rm bit}(x)\) 表示 \(x\

LibreOJ β Round #2貪心只能過樣例 [bitset]【STL】

題目連結:https://loj.ac/problem/515 —————————————————————————————————— 515. 「LibreOJ β Round #2」貪心只能過樣例 記憶體限制:256 MiB 時間限制:1000

#515. LibreOJ β Round #2貪心只能過樣例

解法:記錄 LibreOJ的第一個題,直接bitset暴力即可。 #include <bits/stdc++.h> using namespace std; const int max

[暴力]#514. LibreOJ β Round #2模擬只會猜題意

題目梗概 給定一個長度為 n 的序列 A 。 求一個區間的長度不小於x的最大和。 1≤x≤n≤104,0≤m≤105,∣Ai∣≤104。 解題思路 相信我暴力可以過!!! 因為這題僅

LibreOJ β Round #7匹配字串

                             「LibreOJ β Round #7」匹配字串    

#520. LibreOJ β Round #3緋色 IOI(開端) 貪心

媽耶,沒臉見人了。巨水,想出來不寫,人生重來算了。 就是個找規律題,相鄰一個連一下,但是我沒注意到是IOI賽制,以為是OI賽制所以沒打,感覺70分好打但是懶得了。。 證明就是把相鄰3個列一下式子就出

[結論] LibreOJ #520. LibreOJ β Round #3緋色 IOI(開端)

題意 戳這裡 題解 這是一道結論題。 我們先把數放到數軸上考慮。定義兩個點的距離為幾何上的距離的平方。 我們可以把一個迴路看作兩條從 1 到 n 的不相交的路徑。 有一種經典的二路取數的 O

2018.09.30【LOJ517】LibreOJ β Round #2計算幾何瞎暴力(01Trie)(二進位制拆分)

傳送門 解析: 看到標題的dalaodalaodalao先不要急著錘我。。。 這道題的二進位制拆分和01Trie01Trie01Trie不能混在一起,不要急著說01Trie01Trie01Trie就是二進位制拆分。。。 思路: 這道題可以說是非常好的一道資料結

LOJ #528. LibreOJ β Round #4求和 (莫比烏斯函式)

題意 計算 ∑i=1n∑j=1mμ2(gcd(i,j))∑i=1n∑j=1mμ2(gcd(i,j)) (mod(mod 998244353)998244353) 題解 ∑d=1nμ2(d)

LibreOJ #517.LibreOJ β Round #2計算幾何瞎暴力 字典樹

題意 有一個長度為n的陣列a和m個操作,每個操作形如 1 x在序列的末尾新增一個數x 2 l r詢問[l,r]的數的和 3 x把序列中所有數都異或上x 4把序列中所有數從小到大排序 n,m≤105,x,ai≤109n,m≤105,x,ai≤109

#516. LibreOJ β Round #2DP 一般看規律 set啟發式合併

題意:給出操作,將序列中所有一個數字替換為另一個,詢問每次操作後距離最近的兩個相同數字的距離。 解法:每個數字只與他的前驅和後繼產生貢獻。構建n個set,每次將較小的暴力合併到大的上面,通過lower_bound來找到他的前驅和後繼。懶得離散化可以用map