手套(線段樹+貪心)
阿新 • • 發佈:2017-09-29
int roo pre code 一個 出現 div 比較 編號
1 0 1 0
你現在有N對手套,但是你不小心把它們弄亂了,需要把它們整理一下。N對手套被一字排開,每只手套都有一個顏色,被記為0~N-1,你打算通過交換把每對手套都排在一起。由於手套比較多,你每次只能交換相鄰兩個手套。請你計算最少要交換幾次才能把手套排整齊(只需要手套配對,不需要手套按從小到大的編號排序)。
30%的數據N≤9;
60%的數據N≤1000;
100%的數據N≤200,000。
輸入格式
輸入第一行一個N,表示手套對數。
第二行有2N個整數,描述了手套的顏色。每個數都在0~N-1之間,且每個數字都會出現恰好兩次。
輸出格式
一行,包含一個數,表示最少交換次數。
輸入樣例
21 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; }
手套(線段樹+貪心)