單 single (樹上操作 函式互推)

2021-08-08 17:17:43 字數 2883 閱讀 7708

9.24 達哥

思路:

分析:本題是一道二合一的問題,兩個子任務相互對稱.

演算法1:

t=0的資料最直接的想法是從每個點出發做一遍dfs,時間複雜度o(n^2),可以通過第1個測試點,期望得分10分

演算法2:

t=1的資料最直接的想法是列舉所有可能的a陣列判斷是否可行.第2個測試點n<=5,1<=a[i]<=20.注意20^5=3200000,直接暴力搜尋a[i]的取值是可以承受的,可以通過第2個測試點,期望得分10分,結合演算法1,期望得分20分.

演算法3:

t=1的資料中,b陣列的表示式寫出來之後,每個b[i]對應乙個關於a[i]和樹上兩點距離的方程,例如b[1]=dis(1,1)*a[1]+dis(1,2)*a[2]+dis(1,3)*a[3]+…+dis(1,n)*a[n],於是可以列出n個方程用高斯消元求解,可以通過第2,3,4個測試點,期望得分30分,結合演算法1,期望得分40分.

演算法4:

對於測試點5,樹退化為1條鏈。這個測試點的作用主要在於啟發選手想到正解的思路。

t=0時,我們分別考慮編號小於i和大於i的點對b[i]的貢獻.那麼離得越遠的點對答案的貢獻越大.分別考慮每條邊對答案的貢獻,那麼左側的某條邊對答案的貢獻就是這條邊左側全部a[i]之和.實際上,我們對a[i]分別求取兩次字首和,兩次字尾和即可完成對b[i]的計算.

記suf(i)為a[i]+a[i+1]+….+a[n-1]+a[n],pre(i)為a[1]+a[2]+…+a[i-1]+a[i],則b[i]=pre(1)+pre(2)+…+pre(i-1)+suf(i+1)+suf(i+2)+…+suf(n-1)+suf(n)

考慮t=1的情況.

我們知道suf(2)+suf(3)+…+suf(n)的值(即b[1]),知道pre(1) + suf(3) + suf(4) +…+suf(n)的值(即b[2]),知道pre(1)+pre(2)+suf(4)+…+suf(n)的值(即b[3]),注意到這些式子有很多項是一樣的,考慮作差.可以得到下面的式子:

b[2]-b[1]=pre(1)-suf(2),b[3]-b[2]=pre(2)-suf(3)…..b[i+1]-b[i]=pre(i)-suf(i+1)

這些式子是有實際意義的,考慮從b[1]變到b[2]時答案的變化量,變化的就是1和2之間連邊的貢獻.

同時,記sum=a[1]+a[2]+…+a[n-1]+a[n],顯然pre(i)+suf(i+1)=sum,因此pre(i)=sum-suf(i+1),將上面式子中所有pre換成suf,我們就知道了

sum-2*suf(2) ,sum-2*suf(3)…sum-2*suf(n)的取值。

注意我們將n個式子作差之後得到了n-1個等式,實際上是丟棄了一些資訊,只保留了b[i]之間的相對大小而忽略了b[i]自身的數值大小.於是考慮b[1]=suf(2)+suf(3)+suf(4)+… +suf(n),我們發現suf(2)到suf(n)都恰好出現了一次,而之前得到了n-1個形如sum-2*suf(i)的式子,將這n-1個式子相加,suf(2),suf(3)…suf(n)前面的係數都是2,sum的係數為(n-1),那麼把這個式子加上2*b[1],就得到了(n-1)*sum,將求得的sum代入之前的n-1個式子可以得到suf(2),suf(3),suf(4)……suf(n),差分一下即可得到a陣列.

時間複雜度o(n),可以通過第5個測試點.推出這個做法,樹上的做法也就不難想了.

演算法5(滿分演算法):

考慮樹上的問題.

t=0的時候,我們可以先暴力計算出b[1],然後從b[1]出發開始dfs,由某個點的父親的b[i]推出這個點的b[i],變化的是這個點和父親的連邊的貢獻,也就是這條邊兩側的點的a[i]之和的差值.

t=1的時候,順著鏈上的思路,我們先隨便找乙個點為根建樹,將有邊直接相連的兩個點的b[i]作差.設x的父親為prt[x],以x為根的子樹中所有a[i]之和為sum(x), sum=a[1]+a[2]+…+a[n-1]+a[n],那麼b[x]-b[prt[x]]=(sum-sum(x))-sum(x).

同鏈的情況一樣,得到n-1個式子,將樹根的b[i]也列出式子,可以求出全部a[i]之和,然後就可以根據之前的式子推出所有的a[i],和鏈的做法類似,不再贅述.

#include 

#include

#include

#include

#include

#define ll long long

#define n 100010

using

namespace

std;

int n, opt, idc;

int head[n];

ll sum=0, cnt=0;

ll a[n], b[n], sum[n], dis[n], siz[n];

struct edgeed[n << 1];

inline

void adde (int u, int v)

inline

void dfs0(int u, int f)

}inline

void dfs00(int u, int f)

}inline

void dfs1(int u, int f)

}inline

void dfs11(int u, int f)

} inline

void work0()

inline

void work1()

//42 59 29 34 42 49 51

inline

void init()

int main()

scanf("%d", &opt);

if(opt == 0) work0();

else work1();

}return

0;}

樹上操作 相遇

傳送門 神仙部落格!大概題意就是給定m條路徑,求第i條和前面i 1條中的多少個相交。當路徑a與其他路徑相交,分兩種情況 其他路徑的lca在a上,或者a的lca在其他路徑上。以下先不考慮兩條路徑的lca重合的情況 以下子樹可以轉化為dfs序的乙個區間 第一種情況 在路徑兩個端點上打乙個 1的標記,在l...

bzoj4034 樹上操作

有一棵點數為 n 的樹,以點 1 為根,且樹點有邊權。然後有 m 個 操作,分為三種 操作 1 把某個節點 x 的點權增加 a 操作 2 把某個節點 x 為根的子樹中所有點的點權都增加 a 操作 3 詢問某個節點 x 到根的路徑中所有點的點權和。第一行包含兩個整數 n,m 表示點數和運算元。接下來一...

HAOI2015 樹上操作

題目描述 有一棵點數為 n 的樹,以點 1 為根,且樹點有邊權。然後有 m 個操作,分為三種 操作 1 把某個節點 x 的點權增加 a 操作 2 把某個節點 x 為根的子樹中所有點的點權都增加 a 操作 3 詢問某個節點 x 到根的路徑中所有點的點權和。輸入格式 第一行包含兩個整數 n,m 表示點數...