1. 程式人生 > >BZOJ4989 [Usaco2017 Feb]Why Did the Cow Cross the Road 樹狀數組 逆序對

BZOJ4989 [Usaco2017 Feb]Why Did the Cow Cross the Road 樹狀數組 逆序對

def void math 之間 題解 統計 排列 problem 傳送門

歡迎訪問~原文出處——博客園-zhouzhendong

去博客園看該題解


題目傳送門 - BZOJ4989


題意概括

  一條馬路的兩邊分別對應的序列A、B,長度為n,兩序列為1到n的全排列。當Ai=Bj時,兩邊之間會連一條邊。你可以選擇序列A或序列B進行旋轉(只能使隊尾或隊頭位置上的數字變成隊頭或隊尾上的數字)任意K(0<=K<n)步,如123,可以變成 231 或 312。求旋轉後,最少的邊的交叉數。


題解

  兩個都可以轉,那麽我們只需要分別轉動兩個並統計即可。
  旋轉一個,那麽我們只需要統計逆序對就可以了。對於任意一個情況,逆序對可以nlogn求出,但是如何統計這n種情況呢。我們發現,從一種情況轉到另一種情況,改變的僅是與動的哪一個數字有關的,那麽只需要加加減減就可以轉移了。


代碼

#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cmath>
using namespace std;
typedef long long LL;
const int N=100000+5;
int n,a[N],b[N],c[N],A[N],B[N];
LL tot,ans;
LL min(LL a,LL b){
	return a<b?a:b;
}
int lowbit(int x){
	return x&-x;
}
void add(int x,int d){
	for (;x<=n;x+=lowbit(x))
		c[x]+=d;
}
LL sum(int x){
	int ans=0;
	for (;x>0;x-=lowbit(x))
		ans+=c[x];
	return ans;
}
int main(){
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
		scanf("%d",&a[i]),A[i]=a[i];
	for (int i=1;i<=n;i++)
		scanf("%d",&b[i]),B[i]=b[i];
	for (int i=1;i<=n;i++)
		c[b[i]]=i;
	for (int i=1;i<=n;i++)
		a[i]=c[a[i]];
	memset(c,0,sizeof c);
	tot=0;
	for (int i=n;i>=1;i--){
		tot+=sum(a[i]-1);
		add(a[i],1);
	}
	ans=tot;
	for (int i=n;i>=1;i--){
		tot=tot-(LL)(n-a[i])+(LL)(a[i]-1);
		ans=min(ans,tot);
	}
	for (int i=1;i<=n;i++)
		a[i]=B[i];
	for (int i=1;i<=n;i++)
		b[i]=A[i];
	for (int i=1;i<=n;i++)
		c[b[i]]=i;
	for (int i=1;i<=n;i++)
		a[i]=c[a[i]];
	memset(c,0,sizeof c);
	tot=0;
	for (int i=n;i>=1;i--){
		tot+=sum(a[i]-1);
		add(a[i],1);
	}
	for (int i=n;i>=1;i--){
		tot=tot-(LL)(n-a[i])+(LL)(a[i]-1);
		ans=min(ans,tot);
	}
	printf("%lld",ans);
	return 0;
}

  

BZOJ4989 [Usaco2017 Feb]Why Did the Cow Cross the Road 樹狀數組 逆序對