【LOJ2831】「JOISC 2018 Day 1」道路建设(LCT+树状数组)
- 有\(n\)个点,每个点有一个初始权值。
- 共执行\(n-1\)次操作,每次会选择一个与\(1\)号点连通的点\(x\)和一个与\(1\)号点不连通的点\(y\),在它们之间连一条边。操作的代价是从\(1\)到\(x\)路径上所有点权的逆序对个数,且连边后\(1\)到\(x\)所有点的点权都将变成\(y\)的点权。
- 给出每次的\(x,y\),求每次操作的代价。
- \(n\le10^5\)
\(LCT\)的\(Access\)操作
每次要求出\(1\)到\(x\)的路径并整体赋值,发现这就相当于\(LCT\)的\(Access\)操作,满足\(LCT\)中每棵\(Splay\)维护的所有点权值相同。
因此我们只要在\(Splay\)上维护一个子树大小以及子树赋值标记,询问时暴力往上\(Access\)一波,用一个树状数组求逆序对即可。
一开始离散化写错还调了挺久。。。
代码:\(O(nlog^2n)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
using namespace std;
int n,a[N+5],dc,dv[N+5];
namespace FastIO
{
#define FS 100000
#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
int OT;char oc,FI[FS],FO[FS],OS[30],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('\n');}
}using namespace FastIO;
struct TreeArray
{
int a[N+5];I void U(RI x,CI v) {W(x<=dc) a[x]+=v,x+=x&-x;}//单点修改
I int Q(RI x,RI t=0) {W(x) t+=a[x],x-=x&-x;return t;}//前缀求和
}A;
class LinkCutTree
{
private:
#define PU(x) (O[x].Sz=O[O[x].S[0]].Sz+O[O[x].S[1]].Sz+1)
#define IR(x) (O[O[x].F].S[0]^x&&O[O[x].F].S[1]^x)
#define Wh(x) (O[O[x].F].S[1]==x)
#define Co(x,y,d) (O[O[x].F=y].S[d]=x)
#define PD(x) (O[x].G&&(AS(O[x].S[0],O[x].G),AS(O[x].S[1],O[x].G),O[x].G=0))
#define AS(x,v) (O[x].V=O[x].G=v)
int St[N+5],Pt[N+5],Gt[N+5];struct node {int Sz,V,G,F,S[2];}O[N+5];
I void Ro(RI x) {RI f=O[x].F,p=O[f].F,d=Wh(x);
!IR(f)&&(O[p].S[Wh(f)]=x),O[x].F=p,Co(O[x].S[d^1],f,d),Co(f,x,d^1),PU(f);}
I void S(RI x) {RI f=x,T=0;W(St[++T]=f,!IR(f)) f=O[f].F;
W(T) PD(St[T]),--T;W(!IR(x)) f=O[x].F,!IR(f)&&(Ro(Wh(f)^Wh(x)?x:f),0),Ro(x);PU(x);}
I void Ac(RI x)
{
RI i,o,y,T=0;long long t=0;for(y=0;x;O[x].S[1]=y,PU(x),x=O[y=x].F)
S(x),O[x].S[1]=0,PU(x),t+=1LL*O[x].Sz*A.Q(O[x].V-1),A.U(O[x].V,O[x].Sz),Pt[++T]=O[x].V,Gt[T]=O[x].Sz;
//删去原先的右儿子(注意暂不更新新的右儿子);答案加上当前sz乘上小于当前权值的点数;树状数组上修改
writeln(t);W(T) A.U(Pt[T],-Gt[T]),--T;//清空树状数组
}
public:
I void Init() {for(RI i=1;i<=n;++i) O[i].Sz=1,O[i].V=a[i];}
I void Link(CI x,CI y) {Ac(x),S(x),O[y].F=x,AS(x,O[y].V);}//连边,然后赋值
}LCT;
int main()
{
RI i;for(read(n),i=1;i<=n;++i) read(a[i]),dv[i]=a[i];sort(dv+1,dv+n+1),dc=unique(dv+1,dv+n+1)-dv-1;
for(i=1;i<=n;++i) a[i]=lower_bound(dv+1,dv+dc+1,a[i])-dv;LCT.Init();//离散化
RI x,y;for(i=1;i^n;++i) read(x),read(y),LCT.Link(x,y);return clear(),0;
}
待到再迷茫时回头望,所有脚印会发出光芒