标签:lan space ddl fine 次数 维护 原理 左右 权值线段树
给出一颗有 \(n\) 个叶节点的二叉树,且对于所有非叶节点都有两个儿子,每次操作可以交换任意节点的两个儿子,最终要使得先序遍历后叶节点的逆序对最少,输出这个最小值.
考虑交换某个节点的两个儿子对最终答案的影响,交换之后对于子树内的逆序对并不会产生影响,对这颗子树树以外的逆序对也不会产生影响,所以产生的影响只有两颗子树.那么只需要考虑哪棵子树放前面更优就好了.
考虑用权值线段树来维护每个数出现的次数,考虑合并的时候同时计算出两种情况所产生的逆序对个数.
那么考虑如何快速计算左右子树的两种摆放方式的逆序对个数.可以发现在合并的时候会遍历两子树中区间内都非空很多区间,那么可以通过乘法原理来计算逆序对.
#include<bits/stdc++.h>
#define REP(i,first,last) for(int i=first;i<=last;++i)
#define DOW(i,first,last) for(int i=first;i>=last;--i)
using namespace std;
const int MAXN=2e5+5;
const int MAX_NUM=2e5;
const int INF=1e9;
int n;
long long answer=0;
long long minll(long long a,long long b)
{
if(a<b)
{
return a;
}
return b;
}
struct SegmentTree
{
int lson,rson,sum;
}sgt[MAXN*32];
int sgt_cnt=0;
int rubbish[MAXN*32],rubbish_cnt=0;
#define LSON sgt[now].lson
#define RSON sgt[now].rson
#define MIDDLE ((left+right)>>1)
#define LEFT LSON,left,MIDDLE
#define RIGHT RSON,MIDDLE+1,right
int NewPoint()//防止空间不足,写一个空间回收
{
if(rubbish_cnt)
{
return rubbish[rubbish_cnt--];
}
return ++sgt_cnt;
}
void DeletePoint(int &now)
{
sgt[now].lson=sgt[now].rson=0;
sgt[now].sum=0;
rubbish[++rubbish_cnt]=now;
now=0;
}
void PushUp(int now)//合并标记
{
sgt[now].sum=sgt[LSON].sum+sgt[RSON].sum;
}
void Updata(int place,int &now,int left=1,int right=MAX_NUM)//单点修改
{
if(place<left||right<place)
{
return;
}
if(!now)
{
now=NewPoint();
}
if(left==right)
{
sgt[now].sum++;
return;
}
Updata(place,LEFT);
Updata(place,RIGHT);
PushUp(now);
}
long long add_1,add_2;
void Merge(int &tree1,int &tree2,int left=1,int right=MAX_NUM,bool first=1)//线段树合并
{
if(first)
{
add_1=add_2=0;
}
if(!tree1||!tree2)
{
tree1+=tree2;
tree2=0;
return;
}
if(left==right)
{
sgt[tree1].sum+=sgt[tree2].sum;
DeletePoint(tree2);
return;
}
add_1+=1ll*sgt[sgt[tree1].rson].sum*sgt[sgt[tree2].lson].sum;//通过乘法原理计算逆序对
add_2+=1ll*sgt[sgt[tree1].lson].sum*sgt[sgt[tree2].rson].sum;
Merge(sgt[tree1].lson,sgt[tree2].lson,left,MIDDLE,0);//继续合并左右子树
Merge(sgt[tree1].rson,sgt[tree2].rson,MIDDLE+1,right,0);
DeletePoint(tree2);
PushUp(tree1);
}
#undef LSON
#undef RSON
#undef MIDDLE
#undef LEFT
#undef RIGHT
struct Tree
{
int lson,rson,now;
}tree[MAXN];
int tree_cnt=0;
#define LSON tree[now].lson
#define RSON tree[now].rson
void DFS(int &root)//遍历全树
{
int val;
scanf("%d",&val);
if(val)
{
Updata(val,root);//叶节点只要单点修改就好了
return;
}
int now=++tree_cnt;
DFS(LSON);
DFS(RSON);
Merge(LSON,RSON);//非叶节点需要合并子树
root=LSON;
answer+=minll(add_1,add_2);//并且加上逆序对更少的情况
}
#undef LSON
#undef RSON
int main()
{
scanf("%d",&n);
int root=0;
DFS(root);
printf("%lld",answer);
return 0;
}
「Luogu P3521 [POI2011]ROT-Tree Rotations」
标签:lan space ddl fine 次数 维护 原理 左右 权值线段树
原文地址:https://www.cnblogs.com/Sxy_Limit/p/12631610.html