[loj 2478][luogu P4843]「九省聯考 2018」林克卡特樹
阿新 • • 發佈:2019-03-08
遊戲 for legend tps reat 簡單 block math pap
傳送門
Description
小L 最近沈迷於塞爾達傳說:荒野之息(The Legend of Zelda: Breath of The Wild)無法自拔,他尤其喜歡遊戲中的迷你挑戰。
遊戲中有一個叫做“LCT” 的挑戰,它的規則是這樣子的:現在有一個N 個點的 樹(Tree),每條邊有一個整數邊權vi ,若vi >= 0,表示走這條邊會獲得vi 的收益;若vi < 0 ,則表示走這條邊需要支付- vi 的過路費。小L 需要控制主角Link 切掉(Cut)樹上的 恰好K 條邊,然後再連接 K 條邊權為 0 的邊,得到一棵新的樹。接著,他會選擇樹上的兩個點p; q ,並沿著樹上連接這兩點的簡單路徑從p 走到q ,並為經過的每條邊支付過路費/ 獲取相應收益。
海拉魯大陸之神TemporaryDO 想考驗一下Link。他告訴Link,如果Link 能切掉 合適的邊、選擇合適的路徑從而使 總收益 - 總過路費最大化的話,就把傳說中的大師之劍送給他。
小 L 想得到大師之劍,於是他找到了你來幫忙,請你告訴他,Link 能得到的 總收益 - 總過路費最大是多少。
Solution
原題轉化為樹上求K+1條不相交路徑的最大權值和
是一道WQS二分裸題
首先,可以很容易的寫出一個樹形dp
然後根據wqs的套路,給每條路徑的權值和+add
在計算最大權值和時,我們應當考慮讓選到的邊數盡可能的大
其實就是在寫樹d的時候適當註意一下順序就行
然後,WQS二分出的應該時最小的——使得所選點數\(\geq K+1\)
的add
Code?
#include<bits/stdc++.h> #define ll long long #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();} return x*f; } const ll inf=3e11,MN=3e5+5; int N,K,hr[MN],en; struct edge{int to;ll w;int nex;}e[MN<<1]; inline void ins(int f,int t,int w) { e[++en]=(edge){t,w,hr[f]};hr[f]=en; e[++en]=(edge){f,w,hr[t]};hr[t]=en; } ll ans,dec; struct dp{ ll v;int c; dp(ll _v=0,ll _c=0):v(_v),c(_c){} dp operator+(const dp o){return dp(v+o.v,c+o.c);} dp operator-(const dp o){return dp(v-o.v,c-o.c);} dp operator*(const dp o){return dp(v+o.v-dec,c+o.c-1);} dp operator+(const ll o){return dp(v+o,c);} }f[MN][3]; dp Max(dp o,dp oo){if(o.v>oo.v)return o;return oo;} void dfs(int x,int fa) { register int i; for(i=hr[x];i;i=e[i].nex)if(fa^e[i].to) { dfs(e[i].to,x); f[x][2]=Max(f[x][2]+f[e[i].to][0],f[x][1]+f[e[i].to][1]+dp(e[i].w-dec,-1)); f[x][1]=Max(f[x][0]+f[e[i].to][1]+e[i].w,f[x][1]+f[e[i].to][0]); f[x][0]=f[x][0]+f[e[i].to][0]; } f[x][0]=Max(f[x][0],Max(f[x][1],f[x][2])); } inline void check(ll mid) { register int i;dec=mid; for(i=1;i<=N;++i) f[i][0]=dp(0,0),f[i][2]=f[i][1]=dp(dec,1); dfs(1,0); } int main() { N=read();K=read()+1; register int i,x,y; for(i=1;i<N;++i) x=read(),y=read(),ins(x,y,read()); ll l=-inf,r=inf,mid; for(;l<=r;f[1][0].c>=K?r=mid-1:l=mid+1) { mid=(l+r)>>1;check(mid); if(f[1][0].c>=K)ans=f[1][0].v-1ll*f[1][0].c*mid; } return 0*printf("%lld\n",ans); }
Blog來自PaperCloud,未經允許,請勿轉載,TKS!
[loj 2478][luogu P4843]「九省聯考 2018」林克卡特樹