bzoj2212

http://www.lydsy.com/JudgeOnline/problem.php?id=2212

线段树合并。。。

感觉这东西挺鬼畜的。。。不是很懂

这道题思想很简单,对于每个非叶子结点,我们要合并左右儿子的数,我们可以交换或者不交换,那么交换了其实只会自己内部有影响,对下一次合并没有影响,也就是说我们要看交换了的你需对多还是没交换的逆序对多。。。所以我们维护一个权值线段树,每次合并时左边的size就是左边有多少个数,右边的size就是右边有多少数,那么我们只要比较size[lc[x]]*size[rc[y]]和size[rc[x]]*size[lc[y]]就可以了,然后加上,比较最小值。。。记住是先更新再合并。。。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 6000010;
int cnt, tot, n;
ll size[N], ans, ans1, ans2;
int lc[N], rc[N], v[N], root[N], child[N][2];
void update(int l, int r, int &x, int pos)
{
    x = ++tot; int mid = (l + r) >> 1;
    ++size[x]; if(l == r) return;
    if(pos <= mid) update(l, mid, lc[x], pos);
    else update(mid + 1, r, rc[x], pos);
}
int merge(int x, int y)
{
    if(!x) return y;
    if(!y) return x;
    ans1 += size[lc[x]] * size[rc[y]];
    ans2 += size[rc[x]] * size[lc[y]];
    lc[x] = merge(lc[x], lc[y]);
    rc[x] = merge(rc[x], rc[y]);
    size[x] = size[lc[x]] + size[rc[x]];
    return x;
}
void Init(int u)
{
    scanf("%d", &v[u]);
    if(!v[u])
    {
        child[u][0] = ++cnt; child[u][1] = ++cnt;
        Init(child[u][0]);     Init(child[u][1]);
    }
}
void dfs(int u)
{
    if(v[u]) return;
    dfs(child[u][0]); dfs(child[u][1]);
    ans1 = ans2 = 0;
    root[u] = merge(root[child[u][0]], root[child[u][1]]);
    ans += min(ans1, ans2);
}
int main()
{
    scanf("%d", &n); ++cnt;
    Init(cnt);
    for(int i = 1; i <= cnt; ++i) if(v[i]) update(1, n, root[i], v[i]);
    dfs(1);
    printf("%lld\n", ans);
    return 0;
}

时间: 2024-10-18 03:10:34

bzoj2212的相关文章

神奇的操作——线段树合并(例题: BZOJ2212)

什么是线段树合并? 首先你需要动态开点的线段树.(对每个节点维护左儿子.右儿子.存储的数据,然后要修改某儿子所在的区间中的数据的时候再创建该节点.) 考虑这样一个问题: 你现在有两棵权值线段树(大概是用来维护一个有很多数的可重集合那种线段树,若某节点对应区间是\([l, r]\),则它存储的数据是集合中\(\ge l\).\(\le r\)的数的个数),现在你想把它们俩合并,得到一棵新的线段树.你要怎么做呢? 提供这样一种算法(tree(x, y, z)表示一个左儿子是x.右儿子是y.数据是z的

【bzoj2212&amp;3702】二叉树

线段树合并入门题. 分别计算左子树的逆序对,右子树的逆序对,合并的时候计算贡献. 1 #include<bits/stdc++.h> 2 #define N 8000005 3 using namespace std; 4 typedef long long ll; 5 int n,sz,tot,cnt=0; 6 ll cnt1,cnt2,ans; 7 int val[N],ls[N],rs[N],rt[N],sumv[N],l[N],r[N]; 8 inline int read(){ 9

BZOJ2212: [Poi2011]Tree Rotations

2212: [Poi2011]Tree Rotations Time Limit: 20 Sec  Memory Limit: 259 MBSubmit: 391  Solved: 127[Submit][Status] Description Byteasar the gardener is growing a rare tree called Rotatus Informatikus. It has some interesting features: The tree consists o

bzoj-2212 Tree Rotations

题意: 给出一个二叉树,叶子结点上有权值: 共有n个叶子结点,权值分别为1-n: 现可将所有结点的左右儿子交换,求最小的逆序对数: 2<=n<=200000: 题解: 这题读入有点鬼畜,但是写起来还是比较优雅的: 考虑到一个结点的子树具体形态与它是否和它的兄弟交换是无关的: 所以我们可以像分治一样,先计算子树之间的最小逆序对数: 然后将所有的权值扔到一个线段树中,向它的父亲传递: 它的父亲是没有权值的,我们只是在这里计算这两个线段树之间最小逆序对数,并将其合并就好了: 具体实现实际上只是一个合

BZOJ2212 [Poi2011]Tree Rotations 线段树合并 逆序对

原文链接http://www.cnblogs.com/zhouzhendong/p/8079786.html 题目传送门 - BZOJ3286 题意概括 给一棵n(1≤n≤200000个叶子的二叉树,可以交换每个点的左右子树,要求前序遍历叶子的逆序对最少. 题解 线段树合并. 博主很懒,题解不写了. 这份代码是仿照别人的写的. 代码 #include <cstring> #include <cstdio> #include <cmath> #include <al

bzoj2212 [Poi2011]Tree Rotations 线段树合并

Description Byteasar the gardener is growing a rare tree called Rotatus Informatikus. It has some interesting features: The tree consists of straight branches, bifurcations and leaves. The trunk stemming from the ground is also a branch. Each branch

bzoj2212 Tree Rotations 线段树合并+动态开点

题目传送门 思路: 区间合并线段树的题,第一次写,对于一颗子树,无论这个子树怎么交换,都不会对其他子树的逆序对造成影响,所以就直接算逆序对就好. 注意叶子节点是1到n的全排列,所以每个权值都只会出现1次,合并很好写. 注意动态开点,最多n个叶子节点,然后每次查询用到log个子树节点,(这句话似乎有语病)所以要开nlogn的空间. #include<bits/stdc++.h> #define clr(a,b) memset(a,b,sizeof(a)) #define fpn() freope

大神刷题表

9月27日 后缀数组:[wikioi3160]最长公共子串 dp:NOIP2001统计单词个数 后缀自动机:[spoj1812]Longest Common Substring II [wikioi3160]最长公共子串 [spoj7258]Lexicographical Substring Search 扫描线+set:[poj2932]Coneology 扫描线+set+树上删边游戏:[FJOI2013]圆形游戏 结论:[bzoj3706][FJ2014集训]反色刷 最小环:[poj1734

[转载]hzwer的bzoj题单

counter: 664BZOJ1601 BZOJ1003 BZOJ1002 BZOJ1192 BZOJ1303 BZOJ1270 BZOJ3039 BZOJ1191 BZOJ1059 BZOJ1202 BZOJ1051 BZOJ1001 BZOJ1588 BZOJ1208 BZOJ1491 BZOJ1084 BZOJ1295 BZOJ3109 BZOJ1085 BZOJ1041 BZOJ1087 BZOJ3038 BZOJ1821 BZOJ1076 BZOJ2321 BZOJ1934 BZOJ