洛谷 P3521 [POI2011]ROT-Tree Rotations
题不难,但是我有一个神奇的做法,题解区目前没有,时间相同,空间复杂度甚至更优,但是可惜的是现在不能交题解了。
题意简述略。
首先是一些显然的结论:
1.对于每个非叶节点,我们交换它的两个儿子,只会改变两个元素分别在两个子树内的逆序对数量,不会对别的部分的逆序对数产生影响。
2.如果左子树中有 \(siz_l\) 个元素,右子树中有 \(siz_r\) 个元素,不交换时两子树之间产生的逆序对数是 \(num\) 个,那么交换后的逆序对数就是 \(siz_l*siz_r-num\) 个,换句话说,求交换前后的任意一个就可以算出另外一个了。
那么我们就可以考虑在每个非叶子节点的位置计算其两子树之间的贡献。
考虑合并两个序列并计算之间逆序对数量,第一反应很容易想到类似于归并排序的那个操作,但非常悲伤的是,随便卡卡就可以卡到 \(O(n^2)\),显然过不去。
我们发现归并排序的做法过不去的原因是因为每次合并时的复杂度是 \(O(max(siz_l,siz_r))\),而如果我们基于 \(O(min(siz_l,siz_r))\) 设计算法,总复杂度就只是基于 \(O(nlogn)\) 了,非常容易通过。
那么就是启发式合并了,枚举小的子树内所有元素,查询大子树内比它小的元素的数量,它们的和就是产生的逆序对数(由于只考虑子树大小没有考虑左右所以不一定是交换前/后的,但是这不重要)。
这个时候就见仁见智了,正解在这里使用的动态开点的权值线段树加线段树合并,而我经过一番思索,掏出了pbds里的平衡树,搭配启发式合并和删除返还空间,虽然空间复杂度同阶,我们在实际空间上比标算还要胜上不少。
那么剩下就简单了,直接上代码。
Tips: 注意由于合并子树的值域可能有交(比如说一棵子树内为1,3,另一棵子树为2),所以不能使用 \(join\) 功能。
点击查看代码
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> // 因为tree定义在这里 所以需要包含这个头文件
#include <ext/pb_ds/tree_policy.hpp>
#define N 1000010
#define M 2000010
#define pii pair<int,int>
#define mkp make_pair
#define pb push_back
#define fi first
#define se second
//#define int long long
//#define MOD
#define INF 1061109567
#define int_edge int to[M],nxt[M],head[N],cnt=0;
using namespace std;
using namespace __gnu_pbds;
__gnu_pbds ::tree<int, null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update>T[N];
int n,tot=0,id[N],tmp;
long long ans=0;
//int_edge;void add_edge(int x,int y ){to[++cnt]=y;nxt[cnt]=head[x];head[x]=cnt;}
//int_edge;int val[M];void add_edge(int x,int y,int z){to[++cnt]=y;val[cnt]=z;nxt[cnt]=head[x];head[x]=cnt;}
//int ksm(int X,int P){int Sum;for(Sum=1;P;X=X*X%MOD,P>>=1)if(P&1)Sum=Sum*X%MOD;return Sum;}
void qaq(__gnu_pbds ::tree<int, null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update>&x){
__gnu_pbds ::tree<int, null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update>y;swap(x,y);
}
int dfs(){
scanf("%d",&tmp);
if(tmp){T[++tot].insert(tmp);return tot;}
int ls=dfs(),rs=dfs();
if(T[ls].size()<T[rs].size())swap(ls,rs);
long long sizl=T[ls].size(),sizr=T[rs].size(),sum=0;;
for(auto x:T[rs]){
T[ls].insert(x);int pos=T[ls].order_of_key(x);T[ls].erase(x);
sum+=pos;
}
ans+=min(sum,sizl*sizr-sum);
for(auto x:T[rs])T[ls].insert(x);
qaq(T[rs]);
return ls;
}
signed main()
{
scanf("%d",&n);
dfs();cout<<ans<<endl;
return 0;
}