loj2163 / bzoj2212 / P3521 [POI2011]ROT-Tree Rotations(线段树合并)

P3521 [POI2011]ROT-Tree Rotations

loj2163 [POI2011]ROT-Tree Rotations(数据加强)

(loj的数据套了个fread优化才过...)

显然地,对于一棵线段树(树根设为$rt$),是否翻转它的子树的子树,对于跨$mid$的逆序对数量没有影响。

那么我们可以层层统计(设左右子树为$lc,rc$):

不翻转时,该层(跨$mid$)的逆序对:$a[a[a[rt].lc].rc].sum*a[a[a[rt].rc].lc].sum$

翻转时,逆序对数量:$a[a[a[rt].lc].lc].sum*a[a[a[rt].rc].rc].sum$

递归处理即可。


 

重点是合并线段树:

前提:两棵动态开点线段树

实现(将树$pr$合并到$o$上):

void merge(int &o,int pr){
    if(!o||!pr) {o=o+pr;return;}//一棵为空则返回另一边
    a[o].sum+=a[pr].sum;
    .......//结算信息
    merge(a[o].lc,a[pr].lc);
    merge(a[o].rc,a[pr].rc); //递归合并
}

蓝后就结束了。


 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cctype>
 5 #define re register
 6 using namespace std;
 7 typedef long long ll;
 8 char gc(){
 9     static char buf[100000],*p1=buf,*p2=buf;
10     return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
11 }
12 void read(int &x){
13     char c=gc();x=0;
14     while(!isdigit(c)) c=gc();
15     while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=gc();
16 }//以上读入优化
17 ll min(ll &a,ll &b){return a<b?a:b;}
18 struct node{int sum,lc,rc;}a[4000005];
19 int n,u,rt; ll res1,res2,ans;
20 void update(int &o,int l,int r,int v){
21     if(!o) o=++u;
22     ++a[o].sum;
23     if(l==r) return;
24     int mid=l+((r-l)>>1);
25     if(v<=mid) update(a[o].lc,l,mid,v);
26     else update(a[o].rc,mid+1,r,v);
27 }
28 void merge(int &o,int pr){//把o/pr当作左/右子树,合并到左子树
29     if(!o||!pr) {o=o+pr;return;}
30     a[o].sum+=a[pr].sum;
31     res1+=1ll*a[a[o].lc].sum*a[a[pr].rc].sum; //翻转:lc->lc 和 rc->rc 之间的逆序对数
32     res2+=1ll*a[a[o].rc].sum*a[a[pr].lc].sum; //不翻转:lc->rc 和 rc->lc 之间的逆序对数
33     merge(a[o].lc,a[pr].lc); //合并线段树,并计算 lc->lc 和 rc->lc 之间的逆序对数
34     merge(a[o].rc,a[pr].rc); //同上
35 }
36 void dfs(int &x){//题意的神奇递归输入
37     int q,lc,rc; read(q);
38     if(!q){
39         dfs(lc); dfs(rc);
40         res1=res2=0;
41         merge(x=lc,rc);//合并
42         ans+=min(res1,res2);//选代价小的
43     }else update(x=0,1,n,q);//给叶子结点单独建一棵线段树,后面再合并
44 }
45 int main(){
46 //    freopen("P3521_2.in","r",stdin);
47     read(n); dfs(rt);
48     printf("%lld",ans);
49     return 0;
50 } 
View Code

 

转载于:https://www.cnblogs.com/kafuuchino/p/9839694.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值