ALGO-7_蓝桥杯_算法训练_逆序对

出处:http://blog.csdn.net/enjoying_science/article/details/44114035

 

(有难度,以后回来填坑)

阅读代码中:

  1 #include<stdio.h>
  2 #include<iostream>
  3 using namespace std;
  4 #define ForD(i,n) for(int i=n;i;i--)
  5 #define F (100000007)
  6 #define MAXN (2*200000+10)
  7 long long mul(long long a,long long b){return (a*b)%F;}
  8 long long add(long long a,long long b){return (a+b)%F;}
  9 long long sub(long long a,long long b){return (a-b+(a-b)/F*F+F)%F;}
 10 
 11 int n,root=0;/*n->叶子结点数,root->根序号?*/
 12 struct node
 13 {
 14     int fa;        /*父结点的下标*/
 15     int ch[2];    /*0->左树下标    1->右树下标*/
 16     int size;    /*size->当前结点含有的有效结点数*/ 
 17     int c;        /*当前结点的数值*/
 18     node():size(0),c(0){ch[0]=ch[1]=fa=0;}    /*初始化为0*/
 19 }a[MAXN];                                    /*树形数组->纪录各叶结点的数值*/
 20 
 21 void update(int x)/*更新叶结点个数*/
 22 {
 23     a[x].size=a[a[x].ch[0]].size+a[a[x].ch[1]].size+(a[x].c>0);
 24 }
 25 int tail=0;
 26 void pushdown(int x)/*将叶结点的父结点指向当前结点*/
 27 {
 28     a[a[x].ch[0]].fa=a[a[x].ch[1]].fa=x;
 29 }
 30 
 31 /*创建树*/
 32 void build(int &x)
 33 {
 34     if (!x) x=++tail;
 35     scanf("%d",&a[x].c);
 36     if (a[x].c==0)
 37     {
 38         build(a[x].ch[0]);/*创建左子树*/
 39         build(a[x].ch[1]);/*创建右子树*/
 40         update(x);pushdown(x);/*更新当前结点的有效叶结点个数,以及父结点指向*/
 41     }else a[x].size=1;
 42 }
 43 
 44 void rotate(int x)/*旋转*/
 45 {
 46     int y=a[x].fa,z=a[y].fa;
 47     bool p=a[y].ch[0]==x;
 48     if (z)        /*有爷爷*/
 49     {
 50         if (a[z].ch[0]==y) /*未旋转*/
 51             a[z].ch[0]=x;/*将子树提拔为父树(升序)*/
 52         else 
 53             a[z].ch[1]=x;    /*还原状态*/
 54     }
 55     a[x].fa=z,a[y].fa=x;/*当前结点与父结点交换(父结点指向)*/ 
 56     if (a[x].ch[p])     /*原子树是否有右树(隔代转移)*/
 57         a[a[x].ch[p]].fa=y;
 58     a[y].ch[p^1]=a[x].ch[p];
 59     a[x].ch[p]=y;        /*父树移至子树的右端(右树)*/
 60     update(y);    /*更新旋转后,子树的结点的有效结点数*/
 61 }
 62 
 63 void splay(int x)
 64 {
 65     while (a[x].fa)/*不为根结点*/
 66     {
 67         int y=a[x].fa,z=a[y].fa;
 68         if (z)        /*有爷爷*/
 69             if ((a[y].ch[0]==x)^(a[z].ch[0]==y)) rotate(x);/*旋转*/
 70             else rotate(y);
 71         rotate(x);
 72     }
 73     update(x);
 74 }
 75 void ins(long long &tot,int x,int y)
 76 {
 77     a[x].size++;            /*插入+1*/
 78     if (a[y].c<=a[x].c)        /*是逆序对*/
 79     {
 80         if (a[x].ch[0])     /*左树有子*/
 81             ins(tot,a[x].ch[0],y);
 82         else                 /*左树无子*/
 83             a[y].fa=x,splay(a[x].ch[0]=y);/*右数插入到左树子*/
 84     }
 85     else
 86     {
 87         tot+=a[a[x].ch[0]].size+(a[x].c>0);
 88         if (a[x].ch[1]) ins(tot,a[x].ch[1],y);
 89         else a[y].fa=x,splay(a[x].ch[1]=y);
 90     }
 91 }
 92 
 93 
 94 int q[MAXN],size;
 95 
 96 void clac(int x,int y)
 97 {
 98     if (a[y].ch[0]) clac(x,a[y].ch[0]);
 99     if (a[y].c) q[++size]=y;
100     if (a[y].ch[1]) clac(x,a[y].ch[1]);
101 }
102 
103 long long merge(bool &lor,int z)/*分治*/
104 {
105     int x=a[z].ch[0],y=a[z].ch[1];
106     if (a[x].size<a[y].size) /*判断叶结点多的往前调(平衡树?)*/ 
107         swap(x,y);
108  
109     a[x].fa=0;a[y].fa=0;q[1]=y;
110     size=0;clac(x,y);
111     long long tot=0;    /*最少逆序对*/
112     ForD(i,size)        /*循环(子结点数)次*/
113     {
114         int now=q[i];
115         a[now].ch[0]=a[now].ch[1]=a[now].fa=0;
116         a[now].size=1;
117         ins(tot,x,now);
118         x=now;
119     }
120     a[x].fa=z;
121     a[z].ch[0]=0,a[z].ch[1]=x;
122     return tot;
123 }
124 
125 
126 long long qur(int &x)
127 {
128     if (a[x].c) return 0;/*若根结点没有叶结点,则逆序对为0*/
129     else
130     {
131         long long lson=a[a[x].ch[0]].size,rson=a[a[x].ch[1]].size,ls=qur(a[x].ch[0]),rs=qur(a[x].ch[1]);
132         bool lor=0;
133         long long ms=merge(lor,x);
134         return ls+rs+min(lson*rson-ms,ms);
135     }
136 }
137 int main()
138 {
139     scanf("%d",&n);
140     build(root);
141     cout<<qur(root)<<endl;
142     return 0;
143 }

 

转载于:https://www.cnblogs.com/mind000761/p/8529083.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值