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; }
Nothing is impossible!