JLOI2015 城池攻占(啟發式合併)

2021-09-06 03:22:14 字數 2032 閱讀 4359

傳送門

開始想的是用樹剖來維護

但發現因為對每個人都不一樣而且又要一直修改,不是很好維護

其實也是可以維護的

按照點的dfs

dfsdf

s序從上往下列舉

然後每個點都用樹剖向其到根的路徑上打上自己屬性的標記

這樣相當於每個人就都有走到根的比較了,直接二分找到第乙個跑不了的位置

複雜度o(n

logm

2+ml

ogm2

)o(nlogm^2+mlogm^2)

o(nlog

m2+m

logm

2)但顯然很不好寫,碼量窒息,樹剖+線段樹區間加區間乘區間最大值

還是啟發式合併好寫點

在洛谷上看到了一篇優先佇列啟發式合併的

思路很不錯

確實比左偏樹要簡單許多(c++stl萬歲!)

**:

#include

using

namespace std;

#define ll long long

#define double long double

inline

intread()

while

(isdigit

(ch)

)res=

(res<<3)

+(res<<1)

+(ch^48)

,ch=

getchar()

;return res*f;

}inline ll readl()

while

(isdigit

(ch)

)res=

(res<<3)

+(res<<1)

+(ch^48)

,ch=

getchar()

;return res*f;

}const

int n=

600005

;int adj[n]

,rt[n]

,n,m,nxt[n]

,to[n]

,fa[n]

,dep[n]

,ans[n]

,cnt,a[n]

,st[n]

,des[n]

;ll h[n]

,v[n]

,add[n]

,mul[n]

;struct knt

;priority_queueq[n]

;inline

bool

operator

<

(const knt&a,

const knt&b)

inline ll get

(double x)

inline

void

addedge

(int u,

int v)

inline

void

merge

(int u,

int v));

}}inline

void

dfs(

int u)

while

(q[rt[u]].

size()

)if(!a[u]

)add[rt[u]]+

=v[u]

;else mul[rt[u]]*

=v[u]

,add[rt[u]]*

=v[u];}

intmain()

for(

int i=

2;i<=n;i++

)for

(int i=

1;i<=m;i++))

;}dfs(1)

;for

(int i=

1;i<=n;i++

)for

(int i=

1;i<=n;i++

)for

(int i=

1;i<=m;i++

)exit(0

);}

JLOI2015 城池攻占

霧.改變操作乘法是沒有負數的,那麼就不會改變大小關係,我們就可以dfs樹,然後用可合併堆進行操作。splay 啟發式合併也可以過 luogu 3261 bzoj 4003 include include include define int long long const int maxm 3100...

JLOI2015 城池攻占

點此看題 先把每個人放到對應的點上,可以用左偏樹,然後從下往上合併,每次就刪除當前節點會犧牲的人,然後維護乙個 加法 乘法 標記,時間複雜度o n log n o n log n o nlogn include include using namespace std define int long ...

JLOI2015 城池攻占

原題位址 首先發現乘的時候 係數不會為負,所以能得到乙個關鍵條件 變化後的戰鬥力隨變化前的戰鬥力大小單調 所以我們考慮倍增 設hp x i 是從x開始一路攻克 2 i 個城池所需要最小的初始生命值 設trans x i 0 1 是攻克了 2 i 個城池後攻擊力的變化量,0表示乘,1表示加,先乘後加 ...