### USACO 2017 FEB Platinum mincross 可持久化線段樹

題意

上下有兩個位置分別對應的序列A、B，長度為n，兩序列為n的一個排列。當Ai == Bj時，上下會連一條邊。你可以選擇序列A或者序列B進行旋轉任意K步，如 3 4 1 5 2 旋轉兩步為 5 2 3 4 1。求旋轉后最小的相交的線段的對數。

很暴力的就做了這一題，當選擇A進行旋轉時，A序列翻倍，然后建一棵主席樹，記錄點Bi的度數，為了更新用（其實可以O(1)更新），然后長度為n的區間掃一遍。

B亦同。

#include <cstdio>
#include
<cstdlib>
#include
<cstring>
#include
<string>
#include
<algorithm>
#include
<iostream>

using namespace std;

typedef
long long LL;
const int maxn = 100005*2;
int n, a[maxn], b[maxn], to[maxn];
struct Tree
{
int sum[maxn*40], ls[maxn*40], rs[maxn*40], cnt;
Tree()
{
cnt
= 0;
}
void pushup(int rt)
{
sum[rt]
= sum[ls[rt]]+sum[rs[rt]];
}
void update(int las_rt, int rt, int l, int r, int p, int d)
{
if (l == r)
{
sum[rt]
= sum[las_rt]+d;
return ;
}
int mid = (l+r)>>1;
if (p <= mid)
{
ls[rt]
= ++cnt, rs[rt] = rs[las_rt];
update(ls[las_rt], ls[rt], l, mid, p, d);
}
else
{
ls[rt]
= ls[las_rt], rs[rt] = ++cnt;
update(rs[las_rt], rs[rt], mid
+1, r, p, d);
}
pushup(rt);
}
int query(int rt_1, int rt_2, int l, int r, int L, int R)
{
if (L <= l && r <= R)
return sum[rt_2]-sum[rt_1];
int mid = (l+r)>>1, ret = 0;
if (L <= mid)
ret
+= query(ls[rt_1], ls[rt_2], l, mid, L, R);
if (R > mid)
ret
+= query(rs[rt_1], rs[rt_2], mid+1, r, L, R);
return ret;
}
}T1, T2;
int root1[maxn], root2[maxn];

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]), a[n+i] = a[i];
for (int i = 1; i <= n; ++i)
scanf(
"%d", &b[i]), b[n+i] = b[i];
//part 1
for (int i = 1; i <= n; ++i)
to[b[i]]
= i;
root1[
1] = ++T1.cnt;
T1.update(
0, root1[1], 1, n, to[a[1]], 1);
for (int i = 2; i <= 2*n; ++i)
{
root1[i]
= ++T1.cnt;
T1.update(root1[i
-1], root1[i], 1, n, to[a[i]], 1);
}
LL now_sum
= 0, ans;
for (int i = 1; i <= n; ++i)
if (to[a[i]]+1 <= n)
now_sum
+= T1.query(0, root1[i], 1, n, to[a[i]]+1, n);
ans
= now_sum;
for (int i = n+1; i <= 2*n; ++i)
{
int temp = 0;
if (to[a[i]]-1 >= 1)
temp
= T1.query(root1[i-n], root1[i-1], 1, n, 1, to[a[i]]-1);
now_sum
-= temp, now_sum += (n-temp-1);
ans
= min(ans, now_sum);
}
//part 2
for (int i = 1; i <= n; ++i)
to[a[i]]
= i;
root2[
1] = ++T2.cnt;
T2.update(
0, root2[1], 1, n, to[b[1]], 1);
for (int i = 2; i <= 2*n; ++i)
{
root2[i]
= ++T2.cnt;
T2.update(root2[i
-1], root2[i], 1, n, to[b[i]], 1);
}
now_sum
= 0;
for (int i = 1; i <= n; ++i)
if (to[b[i]]+1 <= n)
now_sum
+= T2.query(0, root2[i], 1, n, to[b[i]]+1, n);
for (int i = n+1; i <= 2*n; ++i)
{
int temp = 0;
if (to[b[i]]-1 >= 1)
temp
= T2.query(root2[i-n], root2[i-1], 1, n, 1, to[b[i]]-1);
now_sum
-= temp, now_sum += (n-temp-1);
ans
= min(ans, now_sum);
}
cout
<<ans <<endl;
return 0;
}