日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

2019ICPC(沈阳) - Fish eating fruit(树形dp+树根转移)

發(fā)布時間:2024/4/11 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 2019ICPC(沈阳) - Fish eating fruit(树形dp+树根转移) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題目鏈接:點擊查看

題目大意:給出一棵樹,給出分類規(guī)則:設兩點之間的距離為w,則按照w對3取模后的余數分為三組(0,1,2),問兩兩頂點之間的距離分類后之和為多少,答案輸出模分別為0,1,2時的距離和

題目分析:這個題目需要我們求以每個點為根節(jié)點跑一遍dfs然后對距離加和,但顯然復雜度為n*n,會T,既然需要每個點都當一次根節(jié)點,我們可以試試用樹根轉移的思想,這樣配合上動態(tài)規(guī)劃,就能將時間復雜度下降到n+n了,就能解決這個問題了,所以問題轉換成了樹形dp的一道題目,我動態(tài)規(guī)劃很菜,代碼是比這網上的大牛的思路寫的,簡單來說,轉移需要兩個數組,一個是num數組,用來記錄子樹中的節(jié)點個數,另一個是dis數組,用來記錄距離,原理大概是記錄每條邊被經過的次數(也就是子節(jié)點的個數),然后乘上邊權,就是將該邊加入到路徑中所貢獻的距離了,然后轉移方程是這樣的:

num[v][0]++;

dis[u][(j+w)%3]+=dis[v][j]+w*num[v][j];(0<=j<=2)

num[u][(j+w)%3]+=num[v][j];(0<=j<=2)

我反正是推不出來這樣的公式。。還是動態(tài)規(guī)劃太弱了啊,大佬太強了太強了

這是求單獨一個節(jié)點為根的轉移方程,再寫一個dfs用來樹根轉移就好了,具體的規(guī)則就是從根節(jié)點開始,然后子節(jié)點和根節(jié)點互換,記得儲存一下原來的數值,等轉移完該子節(jié)點后需要還原,然后進行接下來的狀態(tài)轉移,更詳細的看原大牛的博客吧:

https://blog.csdn.net/weixin_44282912/article/details/100833858

#include<iostream> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=1e4+100;const int mod=1e9+7;struct Node {int to;LL w;Node(int TO,LL W){to=TO;w=W;} };vector<Node>node[N];LL ans[5],dis[N][3],num[N][3];void solve(int u) {for(int i=0;i<3;i++)(ans[i]+=dis[u][i])%=mod; }void dfs1(int u,int fa) {for(int i=0;i<3;i++)dis[u][i]=num[u][i]=0;for(int i=0;i<node[u].size();i++){int v=node[u][i].to;LL w=node[u][i].w;if(v==fa)continue;dfs1(v,u);num[v][0]++;for(int j=0;j<3;j++){(dis[u][(j+w)%3]+=dis[v][j]+w*num[v][j])%=mod;(num[u][(j+w)%3]+=num[v][j])%=mod;}} }void dfs2(int u,int fa) {for(int i=0;i<node[u].size();i++){int v=node[u][i].to;LL w=node[u][i].w;if(v==fa)continue;LL d1[5],d2[5],n1[5],n2[5];for(int j=0;j<3;j++){d1[j]=dis[u][j];d2[j]=dis[v][j];n1[j]=num[u][j];n2[j]=num[v][j];}for(int j=0;j<3;j++){((dis[u][(j+w)%3]-=dis[v][j]+w*num[v][j])+=mod)%=mod;((num[u][(j+w)%3]-=num[v][j])+=mod)%=mod;}num[v][0]--;num[u][0]++;for(int j=0;j<3;j++){(dis[v][(j+w)%3]+=dis[u][j]+w*num[u][j])%=mod;(num[v][(j+w)%3]+=num[u][j])%=mod;}solve(v);dfs2(v,u);for(int j=0;j<3;j++){dis[u][j]=d1[j];dis[v][j]=d2[j];num[u][j]=n1[j];num[v][j]=n2[j];}} }int main() { // freopen("input.txt","r",stdin);int n;while(scanf("%d",&n)!=EOF){for(int i=1;i<=n;i++)node[i].clear();for(int i=0;i<3;i++)ans[i]=0;for(int i=1;i<n;i++){int u,v;LL w;scanf("%d%d%lld",&u,&v,&w);u++;v++;node[u].push_back(Node(v,w));node[v].push_back(Node(u,w));}dfs1(1,-1);solve(1);dfs2(1,-1);printf("%lld %lld %lld\n",ans[0],ans[1],ans[2]);}return 0; }

?

總結

以上是生活随笔為你收集整理的2019ICPC(沈阳) - Fish eating fruit(树形dp+树根转移)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。