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;
}

 

posted @ 2017-02-15 21:13  Splay  阅读(657)  评论(0编辑  收藏  举报