題目鏈接: \
題目
爲什麼牛要過馬路?我們可能永遠都不知道全部原因,但肯定的是,農民約翰的牛經常穿過馬路。事實上,他們經常過馬路,當他們的路徑交叉時,他們經常碰到對方,這是約翰希望改進的情況。
農夫約翰將奶牛分成了N個不同的品種,他的每一個田地都是專門爲一種特定的品種放牧; 例如,專門用於品種奶牛的田地只能用於品種的奶牛,而不能用於任何其他品種的奶牛。一條長長的路穿過他的農場。道路一側有個田地(每個品種一個),道路另一側也有個田地(每個品種也有一個)。所以,當一頭牛穿過馬路,它會穿過特定品種的兩個田地。
如果農民約翰更仔細的研究他的計劃,他會在道路兩旁訂購同品種田地,所以每個品種的兩個田野將直接穿過彼此的道路。這樣就可以讓奶牛隨意過馬路,而不會有不同品種的牛碰撞在一起。唉,但現在道路兩邊的田地可能不一樣,所以農民約翰明白,可能會有一對品種是交叉的當且僅當是一對不同的品種,且道路上一邊田地品種上的任何路線都與道路另一邊田地品種的任何路線相交 。
農民約翰希望儘量減少品種的交叉對數。由於物流方面的原因,他表示,他可以在道路的一邊移動牛,所以那邊的田地可以“循環轉移”。也就是說,對於一個給定的常值來說,循環轉移是指每頭牧場裏的牛都會向前移動個田野,而最前面的只奶牛會移動到最後,因爲向前移動的奶牛已經填補了前個田地。比如,如果道路一側的田地移動前的排列爲,假設爲,新排列將爲.請編程確定在道路一側的田野適當循環移動後存在的品種交叉對的最小值。
輸入
一行包含,接下來行按順序描述了小路一旁田地的品種的號,每一個號是一個在之間的整數。倒數行描述了小路另一旁田地的品種的號。
輸出
循環移動道路側的田野後,請輸出可能的品種交叉對的最小數量(兩邊均可移動)
樣例輸入
5
5
4
1
3
2
1
3
2
5
4
樣例輸出
0
思路
這道題要用樹狀數組。
我們先讓,,那麼就代表在序列中的位置。
那麼我們就可以通過樹狀數組求出的逆序對,而它的逆序對就是當前的交叉對。
那我們只要不斷的把最後面的數移到最前面,然後維護逆序對個數,就可以了。
至於怎麼維護,就是把原來的逆序對。
(一個是把後面拿掉所扣的,另一個是放在前面所加的)
不過還有一個就是,因爲和兩個序列都可以動,所以我們要分別試讓序列動和讓序列動的情況。
(不過好像也不用,但我沒試,不知道能不能只動一個)
代碼
#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long
using namespace std;
int n, a[100001], b[100001], pl[100001], par[100001], ni[100001];
ll ans;
int get(int now) {//單點求值
int answer = 0;
for (; now <= n; now += now & -now)
answer += ni[now];
return answer;
}
void build(int now, int add) {//單點加值
for (; now; now -= now & -now)
ni[now] += add;
}
ll work() {
memset(ni, 0, sizeof(ni));//初始化
ll an = 0;
for (int i = 1; i <= n; i++) {//求出當前逆序對個數
an += get(par[i]);
build(par[i], 1);
}
ll getnew = an;
for (int i = n; i >= 0; i--) {//移數
getnew = getnew - n + par[i] * 2 - 1;//得到新的逆序對的數量
an = min(getnew, an);//找到逆序對最少的情況
}
return an;
}
int main() {
// freopen("mincross.in", "r", stdin);
// freopen("mincross.out", "w", stdout);
scanf("%d", &n);//讀入
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);//讀入
for (int i = 1; i <= n; i++) scanf("%d", &b[i]);//讀入
for (int i = 1; i <= n; i++) pl[b[i]] = i;
for (int i = 1; i <= n; i++) par[i] = pl[a[i]];//初始化
ans = work();
for (int i = 1; i <= n; i++) pl[a[i]] = i;
for (int i = 1; i <= n; i++) par[i] = pl[b[i]];//反過來也要一次(因爲b也可以動)
ans = min(ans, work());
printf("%lld", ans);//輸出
// fclose(stdin);
// fclose(stdout);
return 0;
}