1. 程式人生 > >手套(線段樹+貪心)

手套(線段樹+貪心)

int roo pre code 一個 出現 div 比較 編號

你現在有N對手套,但是你不小心把它們弄亂了,需要把它們整理一下。N對手套被一字排開,每只手套都有一個顏色,被記為0~N-1,你打算通過交換把每對手套都排在一起。由於手套比較多,你每次只能交換相鄰兩個手套。請你計算最少要交換幾次才能把手套排整齊(只需要手套配對,不需要手套按從小到大的編號排序)。

30%的數據N≤9;
60%的數據N≤1000;
100%的數據N≤200,000。

輸入格式

輸入第一行一個N,表示手套對數。
第二行有2N個整數,描述了手套的顏色。每個數都在0~N-1之間,且每個數字都會出現恰好兩次。

輸出格式

一行,包含一個數,表示最少交換次數。

輸入樣例

2
1 0 1 0

輸出樣例

1

將中間兩個手套交換過來,顏色序列變成1 1 0 0。

題解:我們考慮第1只手套,和他配對的是第a只,我們把右邊的移到最左邊總是最優的,如果不是右邊移到最左邊,就可能會出現另一對手套配對時要跨過當前的手套,而我們把右邊的移到最左邊就可以避免這種情況。所以我們每次把右邊的手套移到左邊,但是我們又不能用數組去模擬,那麽怎麽知道右邊的手套移動了多少次呢?

假如枚舉到左邊的手套在a,且未完成配對,右邊的手套在b,則需要移動的次數為(b-a-1)再減去中間已經移到左邊的手套數,對於中間移走的手套數,我們考慮用線段樹維護,以下標為關鍵字,1表示移走,求個區間和就可以了。

#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<fstream>
#include<iostream>
#include<cstring>
using namespace std;

int n,a[400050],id[400020],tree[4500000],vis[400020];
long long ans;

void Updata(int root,int l,int r,int wei,int x)
{
    if
(l==r&&r==wei) { tree[root]+=x; return; } int mid=(l+r)/2; if (wei<=mid) Updata(root*2,l,mid,wei,x); else Updata(root*2+1,mid+1,r,wei,x); tree[root] = tree[root*2]+tree[root*2+1]; } int Query(int root,int l,int r,int L,int R) { if (L<=l&&r<=R) return tree[root]; if (R<l||L>r) return 0; int mid=(l+r)/2; return Query(root*2,l,mid,L,R)+Query(root*2+1,mid+1,r,L,R); } int main() { freopen("2067.in","r",stdin); freopen("2067.out","w",stdout); scanf("%d",&n); for (int i=1; i<=2*n; i++) { scanf("%d",&a[i]); id[a[i]] = i; } for (int i=1; i<=2*n; i++) Updata(1,1,2*n,i,1); for (int i=1; i<=2*n; i++) if (vis[a[i]]==0) { ans = ans+Query(1,1,2*n,i+1,id[a[i]]-1); vis[a[i]]=1; Updata(1,1,2*n,id[a[i]],-1); } printf("%lld\n",ans); return 0; }

手套(線段樹+貪心)