九省聯考2018 秘密襲擊coat 樹形DP

2021-08-18 17:53:31 字數 1833 閱讀 4978

雖然這只是個暴力,正解並不會寫。。。但是這個暴力也讓我長見識了。。。

給定一棵樹,每個節點有乙個權值,求所有的聯通塊中權值為第k大的權值和。

遇到不會做的題目一定要有夢想!!!說不定暴力就a了呢??我們對於每乙個點單獨來考慮對答案的貢獻,即統計包含這個點的聯通塊且這個點為聯通塊中權值第k大的個數。那麼就可以把每個點作為根來考慮一次,大於根節點的權值設為1,其他的全部都是0,現在我們要求的就是乙個樹形依賴揹包容積為k-1的方案數。之前打樹形揹包都是打的nk

2 nk2

,發現現在要統計n遍,那不是炸成n2

k2n 2k

2了。於是找了好久,找到了乙個優化樹形揹包成o(

nk) o(n

k)

的方法,即換一種dp方式。

對於每乙個點,我們強制選擇從根到它的路徑上的所有節點,dp

[u][

j]d p[

u][j

]即表示從根選到u以及這條路徑的左邊和他子樹的dp值,每乙個點首先要強制從父親轉移過來,對於每乙個父親,可以從它的兒子轉移過來,即它的每乙個兒子有選和不選兩種方案,而這裡只要加上dp陣列就可了(具體的看下面的**一定會更好理解)。

但是僅僅是這樣還是不夠的,時間複雜度達到了o(

n2k)

o (n

2k

),發現乙個優化,即我們只對從小到大排序後排名<=k的數進行dp,最後因為資料太水的緣故,o(

(n−k

)∗nk

) o((

n−k)

∗nk)

的演算法輕鬆跑過。

/*********************=

* author : ylsoi

* problem : coat

* algorithm : dp

* time : 2018.4.13

* ***************===*/

#include

#include

#include

#include

#include

using

namespace

std;

void file()

#define rep(i,a,b) for(register int i=a;i<=b;++i)

#define drep(i,a,b) for(register int i=a;i>=b;--i)

#define mrep(i,x) for(register int i=beg[x];i;i=e[i].last)

#define ll long long

#define inf (0x3f3f3f3f)

#define mod (64123)

const

int maxn=1666+10;

int n,k,w,d[maxn],beg[maxn],cnt,ans;

int dp[maxn][maxn],a[maxn];

struct edgee[maxn*2];

void add(int u,int v)

bool judge(int x,int y)

mrep(i,u)//從子樹的狀態轉移到父親

rep(i,0,k-1)dp[f][i]=(dp[f][i]+dp[u][i])%mod;

}void work()

}int main()

rep(i,1,n)a[i]=i;

sort(a+1,a+n+1,judge);

work();

printf("%d\n",ans);

return

0;}

九省聯考 2018 秘密襲擊

題意 給定一顆含 n 個結點的樹,每個點有點權 d i 求所有聯通塊中第 k 大之和。1 leq n,m,k leq 1666,1 leq d i leq m 時間限制 5 秒。題解一道很有趣的題目。做法簡述 由於 dp 為卷積形式對其多項式求點值,並通過類似整體 dp 的方式維護變換,再通過拉格朗...

九省聯考 秘密襲擊

剛開始看起來像樹形dp,卻感覺無從下手 其實正解是fft 每個值的排名往上回溯時都會改變,後效性滿滿的。根本不是一次樹形dp能解決的。那麼,每個值對其他值的排名有什麼影響呢?我們發現只有比val i 大的值才會影響它的排名。不妨每次取乙個點出來,令值大於改點的值變為1,小於改點的值變為0,問題就轉化...

題解 九省聯考 2018 秘密襲擊 coat

可以將危險程度轉化為 列舉權值 t in 1,w 如果某個連通塊權值不小於 t 的節點個數不小於 k 個那麼造成 1 的貢獻。考慮 dp 令 f 表示以 u 為根子樹中包含 u 的連通塊,有 j 個權值不小於 i 的節點的方案數。轉移就是前兩維列舉,第三維做揹包。不妨令 f z sum limits...