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+树根转移)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2019ICPC(徐州) - Color
- 下一篇: 2019ICPC(上海) - Light