HNOI2015 開店 題解

2022-05-16 10:30:59 字數 4401 閱讀 5213

目前只有開o2能過,stl的常數太大了,qwq

題意簡述,給你一棵樹,有點權與邊權,然後有很多詢問,每次詢問你點權在l∼r

l∼r範圍內的點到給點點v

v的距離之和。

我們使用動態點分治來求取答案。

先構建出點分樹,樹高肯定是在log

nlog

n級別內的,然後對於乙個分治點(重心),我們在上面記錄三個值:

我們維護這三個值是因為計算答案時,對於子樹內的點我們可以直接加上,對於祖先部分的點我們直接加上直接到祖先的距離,而對於其它部分的點,我們將其拆分為u→l

ca→v

u→lc

a→v兩條路來統計,然後再減去多餘的。

那麼對應的式子為s2

[u]+

∑(s2

[f[p

]]−s

3[p]

)+(s

1[f[

p]]−

s1[p

])×d

ist(

f[p]

,u)s

2[u]

+∑(s

2[f[

p]]−

s3[p

])+(

s1[f

[p]]

−s1[

p])×

dist

(f[p

],u)

其中pp

開始=u=u

,然後不斷往點分樹的根跳即可。

但是在實際的統計中,我們這樣實現:

下面上一張圖來詳細的講解一下為什麼如此計算:

我們發現對於當前要求的點u

u,如果我們p=u

p=u,那麼就直接加上當前子樹到p

p的距離和,由於還有父親,所以我們看,當p

p為父親v

v時,我們這樣操作:

那麼操作的正確性就非常顯然了。

而對於權值在l∼r

l∼r的限制,我們可以利用字首和的思想,求1∼l

−11∼

l−1和1

∼r1∼

r的答案相減即可得到,所以用乙個set

set或者vec

torv

ecto

r存下來即可(c++中的stl

stl)

那麼每次詢問時即可在點分樹上跳父親統計答案即可,複雜度為log

2nlo

g2n,預處理點分樹nlo

g2nn

log2

n,預處理lca

lca我們用rmq

rmq來,所以為nlo

gnnl

ogn,查詢lca

lca的複雜度變成o(1

)o(1

),所以總的複雜度為nlo

gn+n

log2

n+ml

og2n

nlog

n+nl

og2n

+mlo

g2n,對於2e5

2e5的資料再加上大常數,所以要開o2o

2優化。

**醜陋qaq:

#include

#include

#include

#include

#include

#define ll long long

using

namespace std;

const

int m=

5e5+10;

int n,q,a;

int age[m]

;struct ssss(

int a,

int b,ll c):to

(a),

last

(b),

len(c)

}g[m<<1]

;int head[m]

,cnt;

void

add(

int a,

int b,ll c)

struct node

node

(int a,ll b,ll c,ll d):p

(a),

s1(b),s2

(c),

s3(d)

bool

operator

<

(const node &a)

const};

vector rec[m]

;#define pb push_back

typedef vector

::iterator iter;

int pos[m]

,tim,lgp[m]

,lg;

ll lcaq[25]

[m],dis[m]

;//rmq求lca來求距離

void

dfs_rmq

(int a,

int b)

}void

init_rmq()

}}ll dist

(int a,

int b)

int sze[m]

,f[m]

,son[m]

,totsze,root;

bool vis[m]

;//找重心

void

findroot

(int a,

int fa)

if(totsze-sze[a]

>son[a]

)son[a]

=totsze-sze[a];if

(son[a]

)root=a;

}void

getall

(int a,

int fa,

int o)

}void

find

(int a)

//字首和

for(

int i=head[a]

;i;i=g[i]

.last)

}node calc

(int o,

int l,

int r)

ll getans

(int o,

int l,

int r)

return ans;

}ll zans;

ll a,b,c;

intmain()

dfs_rmq(1

,0);

init_rmq()

; root=

0;son[0]

=m;totsze=n;

findroot(1

,0);

find

(root)

;while

(q--

)return0;

}

我們可以發現,乙個詢問點的答案為如下式子:(∑

i=1n

dis(

i))+

n×di

s(u)

−2×∑

i=1n

dis(

lca(

i,u)

)(i=

1∑n​

dis(

i))+

n×di

s(u)

−2×i

=1∑n

​dis

(lca

(i,u

))其中dis(

i)di

s(i)

表示ii

到根節點的距離。

這個式子和動態點分治中維護的資訊是十分類似的,我們開始先加上所有點到根的距離,然後對於一部分點v

v,它必須走v→l

ca→u

v→lc

a→u這條路,所以還有根節點回來的路徑為n×d

is(u

)n×d

is(u

),但是這樣無疑會多算一些邊,所以我們利用差分的思想,一條路影響是不會超過lca

lca的上方,所以我們減去lca

lca到根的距離,由於一條路是兩個點,所以減兩次,那麼便是答案了。

對於資訊的維護,我們用線段樹+樹鏈剖分即可,但是有點權l∼r

l∼r的限制,所以我們用主席樹即可做到維護,log

2nlo

g2n的查詢。

但是此題空間限制較小(對於主席樹來說),所以我們使用標記永久化,減少新的節點的開銷。

下面上**的連線我沒有打主席樹的**,所以借用的他人的

【in-luogu】

複雜度o(n

+nlo

g2n+

mlog

2n)o

(n+n

log2

n+ml

og2n

),常數較為小一點(因為沒有使用過多的stl

stl)。

HNOI2015實驗比較 題解

記得幾個月前自己曾經做過一道關於一張dag求排列的問題,現如今遇到了的是一道關於樹求排列的問題.這乙個問題看似簡單實際上有乙個細節那就是他可以取 這就使得複雜度公升高了.首先,最簡單的插孔原理。這乙個非常簡單,做這道題時,蒟蒻我現推式子。為什麼要解決這個問題呢,因為最基本的,在不考慮等於的情況下,n...

HNOI2015 菜餚製作 題解

傳送門 hnoi2015 菜餚製作 根據題目的描述,我們要做的首先是一步一步的分析 這個題肯定是拓撲排序無疑,因為要求的是滿足固定條件 有向圖的連邊 的某種排列,而難點就在於如何拓撲排序。根據題目的描述,我們首先想到的是根據字典序大小進行排序,那麼我們就舉例幾組資料來驗證一下是否正確,那麼先看下面這...

HNOI2015 接水果 題解

hnoi2015 接水果 給你乙個樹上路徑集合 s 每條路徑有個權值。每次詢問一條路徑 x,y 問它在 s 中包含的路徑中權值第 k 小的是多少。首先考慮如何判斷判斷一條路徑是否被另一條路徑包含。當一條路徑 x,y dep x dep y 是一條深度從淺到深的鏈時,路徑 u,v dep u dep ...