【HNOI】 lct tree-dp
【題目描述】給定2-3顆樹(shù),每個(gè)邊的邊權(quán)為1,解決以下獨(dú)立的問(wèn)題。
現(xiàn)在通過(guò)連接若干遍使得圖為連通圖,并且Σdis(x,y)最大,x,y只算一次。
每個(gè)點(diǎn)為黑點(diǎn)或者白點(diǎn),現(xiàn)在需要?jiǎng)h除一些邊,使得圖中的黑點(diǎn)度數(shù)為奇數(shù),白點(diǎn)為偶數(shù),要求刪除的邊最多。
【數(shù)據(jù)范圍】 100% n<=10^5
首先我們來(lái)解決第一問(wèn),因?yàn)槊考右粭l邊就可能使得若干點(diǎn)到其他點(diǎn)的距離變小,那么我們需要加盡量少的邊來(lái)使得圖連通。
設(shè)dis_[x]為x在x所在子樹(shù)中,x到其他所有點(diǎn)的距離,這個(gè)我們可以通過(guò)設(shè)dis[x]表示x到x子樹(shù)中所有點(diǎn)的距離和來(lái)由父節(jié)點(diǎn)轉(zhuǎn)移得到。
那么答案可以分為兩部分,分別為樹(shù)中的點(diǎn)對(duì)距離和跨樹(shù)的點(diǎn)對(duì)距離,前一個(gè)問(wèn)題比較容易,可以通過(guò)dis_[x]或者計(jì)算每條邊被經(jīng)過(guò)的次數(shù)來(lái)求出。
那么對(duì)于兩顆樹(shù)的情況,我們就需要連接這兩棵樹(shù)中dis值最大的兩個(gè)點(diǎn),假設(shè)為x,y。這樣答案就是 ? ? ?dis[x]*size[tree_y]+dis[y]*size[tree_x]+size[tree_x]*size[tree_y],這個(gè)由連接的那條邊的被經(jīng)過(guò)次數(shù)可以得出。
那么現(xiàn)在考慮三棵樹(shù)的情況,我們需要枚舉中間的樹(shù),這樣左右兩棵樹(shù)肯定連接dis最大的點(diǎn),中間的連接的則不確定,我們可以列出來(lái)整個(gè)答案的表達(dá)式,設(shè)左面的樹(shù)和中間的樹(shù)通過(guò)x,y點(diǎn)連通,中間的點(diǎn)和右面的樹(shù)通過(guò)u,v點(diǎn)連接,設(shè)三棵樹(shù)的size為size[1],size[2],size[3],y與u點(diǎn)的距離為d[y][u],那么答案就是size[1]*dis_[y]+size[2]*dis_[x]+size[1]*size[2]+size[3]*dis_[u]+size[2]*dis_[v]+size[1]*dis_[v]+size[3]*dis_[u]+size[1]*size[3]*(d[y][u]+2)
我們可以發(fā)現(xiàn),這個(gè)中與y,u點(diǎn)有關(guān)的式子可以寫成a*dis_[y]+b*dis_[u]+c*d[u][v]的形式,其中a,b,c為常數(shù),那么對(duì)于這個(gè)我們就可以用tree-dp搞出來(lái),記錄點(diǎn)x的子樹(shù)中dis_[p]+(d[x][p]+1)*c的最大值,然后不斷的更新答案就可以了。
第二問(wèn)比較簡(jiǎn)單,我們可以貪心的來(lái)想,對(duì)于一棵樹(shù),我們從葉子節(jié)點(diǎn)開(kāi)始,因?yàn)槿~子節(jié)點(diǎn)的度數(shù)為1,那么我們只需要判斷葉子節(jié)點(diǎn)的顏色,就可以判斷這個(gè)點(diǎn)和其父節(jié)點(diǎn)的邊是否可以刪掉。
反思:開(kāi)始寫tree-dp維護(hù)中間樹(shù)的值的時(shí)候沒(méi)有考慮到一些特殊情況,比如連接的y,u點(diǎn)其中一點(diǎn)是另一點(diǎn)的祖先,還有開(kāi)始覺(jué)得如果中間的樹(shù)選擇兩個(gè)點(diǎn)肯定不能是同一點(diǎn),所以邊界就處理的不是特別好,但是可能會(huì)有某些點(diǎn)單獨(dú)構(gòu)成樹(shù),這樣的話就必須連接同一個(gè)點(diǎn)。第二問(wèn)還是比較容易寫的。
//By BLADEVIL #include <cstdio> #include <cstring> #include <algorithm> #define maxn 100010 #define LL long longusing namespace std;LL n,m,l; LL a[maxn],pre[maxn<<1],other[maxn<<1],last[maxn],col[maxn],rot[4],num[maxn<<1],flag[maxn]; LL dis_[maxn],dis[maxn],size[maxn],ans[maxn],ANS[4],max_a[maxn],max_b[maxn],cnt[maxn];void connect(LL x,LL y,LL z) {pre[++l]=last[x];last[x]=l;other[l]=y;num[l]=z; }void paint(LL x,LL fa,LL c) {col[x]=c;for (LL p=last[x];p;p=pre[p]) {if (other[p]==fa) continue;paint(other[p],x,c);} }void make_dis(LL x,LL fa) {dis[x]=0; size[x]=1;for (LL p=last[x];p;p=pre[p]) {if (other[p]==fa) continue;make_dis(other[p],x);dis[x]+=dis[other[p]]+size[other[p]];size[x]+=size[other[p]];} }void make_dis_(LL x,LL fa,LL s) {if (fa!=-1) dis_[x]=dis_[fa]-size[x]-dis[x]+s-size[x]+dis[x]; else dis_[x]=dis[x];for (LL p=last[x];p;p=pre[p]) {if (other[p]==fa) continue;make_dis_(other[p],x,s);} }void calc(LL x,LL fa,LL s) {for (LL p=last[x];p;p=pre[p]) {if (other[p]==fa) continue;ANS[col[x]]+=size[other[p]]*(s-size[other[p]]);calc(other[p],x,s);} }void dp(LL x,LL fa,LL a,LL b,LL c,LL &Ans) {max_a[x]=dis_[x]*a+c; max_b[x]=dis_[x]*b+c;for (LL p=last[x];p;p=pre[p]) {if (other[p]==fa) continue;dp(other[p],x,a,b,c,Ans);max_a[x]=max(max_a[x],max_a[other[p]]+c);max_b[x]=max(max_b[x],max_b[other[p]]+c);}LL aa=0,bb=0;//printf("%d %d\n",max_a[x],max_b[x]);for (LL p=last[x];p;p=pre[p]) {if (other[p]==fa) continue;Ans=max(Ans,max_a[x]+c+dis_[x]*b);Ans=max(Ans,max_b[x]+c+dis_[x]*a);}//printf("%d %d\n",x,Ans);for (LL p=last[x];p;p=pre[p]) {if (other[p]==fa) continue;if (max_a[other[p]]>max_a[aa]) aa=other[p];if (max_b[other[p]]>max_b[bb]) bb=other[p];}//printf("%d %d\n",x,Ans);for (LL p=last[x];p;p=pre[p]) {if (other[p]==fa) continue;if (other[p]!=aa) Ans=max(Ans,max_a[aa]+max_b[other[p]]+(c<<1));//printf("%d %d %d %d\n",Ans,max_a[aa],max_b[other[p]],c<<1);if (other[p]!=bb) Ans=max(Ans,max_b[bb]+max_a[other[p]]+(c<<1));}//printf("%d %d\n",aa,max_a[aa]);//printf("%d %d\n",x,Ans); }LL work(LL le,LL x,LL ri) {LL a=size[le],b=size[ri],c=size[le]*size[ri],ans=0;LL cur[4]; cur[1]=cur[2]=cur[3]=0;for (LL i=1;i<=n;i++) cur[col[i]]=max(cur[col[i]],dis_[i]);//printf("fuck %d %d\n",col[le],col[ri]);ans=cur[col[le]]*size[x]+a*size[x]+cur[col[ri]]*size[x]+size[x]*b+a*cur[col[ri]]+b*cur[col[le]];//printf("fuck\n");memset(max_a,0,sizeof max_a);memset(max_b,0,sizeof max_b);LL Ans=-1;dp(x,-1,a,b,c,Ans);Ans=max(Ans,c<<1);//printf("%d %d\n",ans,Ans);ans+=Ans;//printf("%d\n",ans);return ans; }void Work(LL x,LL fa) {for (LL p=last[x];p;p=pre[p]) {if (other[p]==fa) continue;Work(other[p],x);}//printf("%d %d %d\n",x,a[x],cnt[x]);if (a[x]^cnt[x]) {for (LL p=last[x];p;p=pre[p]) if (other[p]==fa) flag[num[p]]=1;cnt[fa]^=1;}; }int main() {freopen("lct.in","r",stdin); freopen("lct.out","w",stdout);scanf("%lld%lld\n",&n,&m);char c;for (LL i=1;i<=n;i++) scanf("%c",&c),a[i]=(c=='B')?1:0;for (LL i=1;i<=m;i++) {LL x,y;scanf("%lld%lld",&x,&y);connect(x,y,i); connect(y,x,i);}LL sum=0;for (LL i=1;i<=n;i++) if (!col[i]) paint(i,-1,++sum),rot[sum]=i;for (LL i=1;i<=3;i++) if (rot[i]) make_dis(rot[i],-1),make_dis_(rot[i],-1,size[rot[i]]);for (LL i=1;i<=3;i++) if (rot[i]) calc(rot[i],-1,size[rot[i]]);//for (LL i=1;i<=n;i++) printf("%d ",col[i]); printf("\n");//printf("%d %d %d\n",rot[1],rot[2],rot[3]);//for (LL i=1;i<=n;i++) printf("%d %d %d %d\n",i,dis[i],dis_[i],size[i]);//for (LL i=1;i<=3;i++) printf("%d ",ANS[i]); printf("\n");if (sum==2) {LL cur[3];cur[1]=cur[2]=0;for (LL i=1;i<=n;i++) cur[col[i]]=max(cur[col[i]],dis_[i]);LL Ans=ANS[1]+ANS[2]+cur[1]*size[rot[2]]+cur[2]*size[rot[1]]+size[rot[1]]*size[rot[2]];printf("%lld\n",Ans);//printf("%d %d\n",cur[1],cur[2]);//printf("%d %d\n",size[rot[1]],size[rot[2]]);//printf("%d %d\n",ANS[1],ANS[2]);} else {LL Ans=0;Ans=max(Ans,work(rot[2],rot[1],rot[3]));Ans=max(Ans,work(rot[1],rot[2],rot[3]));Ans=max(Ans,work(rot[1],rot[3],rot[2]));//printf("%d\n",Ans);Ans+=ANS[1]+ANS[2]+ANS[3];printf("%lld\n",Ans);}for (LL i=1;i<=n;i++) for (LL p=last[i];p;p=pre[p]) cnt[other[p]]++,cnt[i]++;//for (LL i=1;i<=n;i++) printf("%d\n",cnt[i]);//for (LL i=1;i<=n;i++) printf("%d\n",a[i]);for (LL i=1;i<=n;i++) cnt[i]/=2,cnt[i]%=2;for (LL i=1;i<=3;i++) Work(rot[i],-1);LL ans_=0;for (LL i=1;i<=m;i++) if (!flag[i]) ans_++;printf("%lld\n",ans_);for (LL i=1;i<=m;i++) if (!flag[i]) printf("%lld ",i); printf("\n");fclose(stdin); fclose(stdout);return 0; }?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/BLADEVIL/p/3625012.html
總結(jié)
以上是生活随笔為你收集整理的【HNOI】 lct tree-dp的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: VARIANT变体类型数据
- 下一篇: ESXi主机管理内存资源的方式