給定一棵有n個節點的樹 n<=10^5
需要支援2種操作
q u v 詢問從u到v路徑上所有點的權值和
c u v 改變u的權值為v
操作次數m<=2*10^5
無腦樹鏈剖分,然後直接用bit維護就可以了
但是這題的操作比較簡單,使用樹鏈剖分太大才小用了..
樹鏈剖分,顧名思義,將樹上的鏈進行適當的剖分使得其能被資料結構高效的維護
我們如果將操作對鏈的修改或對鏈的影響進行適當的轉化,那麼就不需要進行樹鏈剖分了
我們先對整棵樹進行dfs,在進行dfs的時候,將每個點的點權儲存到該點到其父親的邊上,將原來的對點修改問題轉化為對邊修改問題。
接下來考慮詢問操作,如果詢問的是點對(u,v),我們可以將其拆成3個詢問
假設樹根編號為1,那麼詢問的答案就是sum(1,u)+sum(1,v)-2*sum(1,lca(u,v))+value[lca(u,v)]
lca(u,v)可以通過樹上倍增找到
sum(1,x)表示從樹根到節點x的所有邊的權值和
能否利用dfs序來維護出sum(1,x)呢..?
答案是,可以。
在dfs遍歷節點u時,u有一條連向兒子v的邊,那麼此時就將邊u->v加入dfs序之中(標記該邊的時間為time[v]),且邊的權值為value[v]
當遍歷完v的子樹返回到u時,再往dfs序中新增一條從v返回u的邊,權值為-valuev
那麼從dfs序中字首和[1,time[x]]就是sum(1,x)
(這是因為訪問過的不在(1,x)路徑上的邊所帶的權值均被它的一條反向邊抵消)
修改的時候,只要將邊time[x]和last[x]的權值均修改下即可了
用樹狀陣列能高效維護上述的字首查詢和點修改操作
總時間複雜度o((n+m)log n)
#include
#define fo(i,a,b) for(int i=a;i<=b;i++)
const
int mn=100000+50,bi=18;
int n,m,v[mn],be[mn],bb[bi+1],x,y,et,tt,tn,de[mn],f[mn][bi+1],ti[mn],tl[mn],d[mn*2];//ti[x]表示訪問father(x)->x的邊編號 tl[x]表示x->father(x)編號
struct edge;
edge e[mn*2];
char ch[10];
void insert(int x,int v)
return ans;
}void add_edge(int x,int y)
void dfs(int x,int fa)
else
break;
int i=be[x];
while (i)
i=e[i].ne;
}}int lca(int x,int y)
if (x==y)
return ans-ask(ti[x])*2+v[x];
for(int i=bi;i>=0;i--)
if ((f[x][i])&&(f[x][i]!=f[y][i]))
x=f[x][0];
return ans-ask(ti[x])*2+v[x];
}int main()
fo(i,2,n)
ti[1]=++tt;
insert(ti[1],v[1]);
de[1]=1;
dfs(1,-1);
tl[1]=++tt;
scanf("%d",&m);
fo(i,1,m)
if (ch[0]=='c')
}return
0;}
Vijos P1986 小h的妹子樹二
背景 小h同學的妹子實在是太多辣,多的他又種下了一顆妹子樹。描述樹上有n個妹子,兩兩之間有且僅有一條路徑。小h每次想泡兩個妹子,但是妹子是一種奇怪的生物 霧 小h無論想泡哪一對妹子,都得為她們路徑上的所有妹子支付軟妹幣 p.s.泡乙個不就好了 這次我們的妹子樹形態不會變了,但是她們需要的軟妹幣會變化...
小H的詢問(線段樹)
線段樹需要維護的 最大有效子區間權值和,左端最大有效子區間權值和,右端最大有效子區間權值和,區間和,本區間有效性。include include include include include include include define maxn 1000005 using namespace s...
問題 H 小k的簡單問題
時間限制 1 sec 記憶體限制 128 mb 提交 107 解決 57 提交 狀態 命題人 jsu admin 題目描述 地圖上有n個村莊,小k每個月需要往每個村莊運送數量不等的糧食,運送糧食到任意乙個村莊需要消耗的費用為距離的平方乘以糧食的重量,現在小k打算在地圖上建立糧食基地,請問將基地建在哪...