傳送門
題目的意思描述非常明確,我們很容易想到最暴力的演算法——模擬!
不知道o(nm)的模擬能拿到多少分,反正肯定會t飛的。
我們考慮優化一下,因為各個騎士之間是獨立的,但是我們的問題是乙個乙個列舉騎士複雜度過高,我們可以用全域性變數來記錄一下每乙個騎士團的情況。這樣的話,我們對於每乙個被攻擊的城池建立一棵左偏樹,維護當前攻占到這個城池的騎士,然後把戰鬥力小於城池防禦力的騎士踢出去即可。
刪除的複雜度最多是o(mlogm),建立左偏樹最多也是o(mlogm),瓶頸出現在兩棵左偏樹合併上。因為不同的騎士團攻打到同乙個城池的時候,他們之前獲得的buff可能是不同的,我們不能把兩個騎士團的情況全都計算之後再正常合併,那樣又會t的。解決的方法就是像線段樹一樣打上標記,注意要先乘後加(這個就和線段樹模板是一樣的了),每次在合併和刪除操作的時候都要下放標記。
這樣合併就可以了,最後我們記錄答案的話,在向下dfs的過程中記錄一下每個點的深度,之後就能知道這個騎士一共攻占過幾個城池了。
看一下**。
#include#include#include
#include
#include
#include
#include
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')
using
namespace
std;
typedef
long
long
ll;const
int m = 300005
;const
int inf = 1000000009
;ll read()
while(ch >= '
0' && ch <= '9'
)
return ans *op;
}struct
edge
e[m<<1
];int
n,m,c[m],f[m],dead[m],lc[m],rc[m],size[m],dep[m],kill[m],ecnt,head[m],root[m],dis[m];
ll h[m],v[m],s[m],mul[m],add[m],sum[m];
bool
ml[m];
void adde(int x,int
y)void cover(int
x,ll a,ll b)
void pushdown(int
x)int merge(int x,int
y)int split(int
x)void dfs(int x,int
fa)
if(ml[x]) cover(root[x],v[x],0
);
else cover(root[x],1
,v[x]);
}int
main()
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表示加,先乘後加 ...