雖然這只是個暴力,正解並不會寫。。。但是這個暴力也讓我長見識了。。。
給定一棵樹,每個節點有乙個權值,求所有的聯通塊中權值為第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...