P3521 [POI2011]ROT-Tree Rotations - 线段树合并
题解
考虑对于每个点都开一棵动态开点线段树,以点权为下标、个数为值,记录以这个点为根的子树内的信息。
可以发现交换某个点 \(u\) 的代价可以快速计算:在把左、右儿子的线段树合并上来的时候,对于这两棵线段树上的对应点 \(p,q\),不交换的代价会增加 \(v_{\mathrm{rson}(p)}\times v_{\mathrm{lson}(q)}\),交换的代价会增加 \(v_{\mathrm{lson}(p)}\times v_{\mathrm{rson}(q)}\)。因为每个点选择是否交换对其祖先节点并无影响,所以每个点都贪心地选择代价最小的方案即可。
代码
#include <cstdio>
#include <cstring>
#include <cctype>
#include <vector>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
template<typename T> void Read(T &x){
x=0;int _f=1;
char ch=getchar();
while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar();
while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
x=x*_f;
}
template<typename T,typename... Args> void Read(T &x,Args& ...others){
Read(x);Read(others...);
}
typedef long long ll;
const int N=6e5+5;
int n,leaf,son[N][2],w[N];
int ReadTree(){
int x,u=++n;Read(x);
if(x) w[u]=x;
else{
son[u][0]=ReadTree();son[u][1]=ReadTree();
}
return u;
}
#define ls(xx) t[xx].ls
#define rs(xx) t[xx].rs
int tot=0;
struct Node{
int l,r,ls,rs,v;
}t[N*18];
int Newnode(int l,int r){
t[++tot]=Node{l,r,0,0,0};
return tot;
}
void Pushup(int p){t[p].v=t[ls(p)].v+t[rs(p)].v;}
void Add(int p,int pos,int x){
if(t[p].l==t[p].r){
t[p].v+=x;return;
}
int mid=(t[p].l+t[p].r)>>1;
if(pos<=mid){
if(!ls(p)) ls(p)=Newnode(t[p].l,mid);
Add(ls(p),pos,x);
}else{
if(!rs(p)) rs(p)=Newnode(mid+1,t[p].r);
Add(rs(p),pos,x);
}
Pushup(p);
}
int Merge(int u,int v,ll &x,ll &y){
if(!u||!v) return u^v;
if(t[u].l==t[u].r){t[u].v+=t[v].v;return u;}
x+=1LL*t[rs(u)].v*t[ls(v)].v,y+=1LL*t[rs(v)].v*t[ls(u)].v;
ls(u)=Merge(ls(u),ls(v),x,y),rs(u)=Merge(rs(u),rs(v),x,y);
Pushup(u);return u;
}
ll Solve(int u,int &rt){
if(w[u]){
rt=Newnode(1,leaf);
Add(rt,w[u],1);return 0;
}
int rt1,rt2;ll x=0,y=0,res=0;
res+=Solve(son[u][0],rt1);res+=Solve(son[u][1],rt2);
rt=Merge(rt1,rt2,x,y);
return res+min(x,y);
}
int main(){
Read(leaf);ReadTree();
int temp;
printf("%lld\n",Solve(1,temp));
return 0;
}
Written by Alan_Zhao