BZOJ 1063: [Noi2008]道路设计 树形DP

来源:互联网 发布:琳琅怎么绑定淘宝账号 编辑:程序博客网 时间:2024/05/22 00:30

Description

  Z国坐落于遥远而又神奇的东方半岛上,在小Z的统治时代公路成为这里主要的交通手段。Z国共有n座城市,一
些城市之间由双向的公路所连接。非常神奇的是Z国的每个城市所处的经度都不相同,并且最多只和一个位于它东
边的城市直接通过公路相连。Z国的首都是Z国政治经济文化旅游的中心,每天都有成千上万的人从Z国的其他城市
涌向首都。为了使Z国的交通更加便利顺畅,小Z决定在Z国的公路系统中确定若干条规划路线,将其中的公路全部
改建为铁路。我们定义每条规划路线为一个长度大于1的城市序列,每个城市在该序列中最多出现一次,序列中相
邻的城市之间由公路直接相连(待改建为铁路)。并且,每个城市最多只能出现在一条规划路线中,也就是说,任意
两条规划路线不能有公共部分。当然在一般情况下是不可能将所有的公路修建为铁路的,因此从有些城市出发去往
首都依然需要通过乘坐长途汽车,而长途汽车只往返于公路连接的相邻的城市之间,因此从某个城市出发可能需要
不断地换乘长途汽车和火车才能到达首都。我们定义一个城市的“不便利值”为从它出发到首都需要乘坐的长途汽
车的次数,而Z国的交通系统的“不便利值”为所有城市的不便利值的最大值,很明显首都的“不便利值”为0。小
Z想知道如何确定规划路线修建铁路使得Z国的交通系统的“不便利值”最小,以及有多少种不同的规划路线的选择
方案使得“不便利值”达到最小。当然方案总数可能非常大,小Z只关心这个天文数字modQ后的值。注意:规划路
线1-2-3和规划路线3-2-1是等价的,即将一条规划路线翻转依然认为是等价的。两个方案不同当且仅当其中一个方
案中存在一条规划路线不属于另一个方案。
Input

  第一行包含三个正整数N、M、Q,其中N表示城市个数,M表示公路总数,N个城市从1~N编号,其中编号为1的是首都
。Q表示上文提到的设计路线的方法总数的模数。接下来M行,每行两个不同的正数ai、bi(1≤ai,bi≤N)表示有一条
公路连接城市ai和城市bi。输入数据保证一条公路只出现一次。
Output

  包含两行。第一行为一个整数,表示最小的“不便利值”。第二行为一个整数,表示使“不便利值”达到最小时
不同的设计路线的方法总数modQ的值。如果某个城市无法到达首都,则输出两行-1。
Sample Input
5 4 100

1 2

4 5

1 3

4 1
Sample Output
1

10
HINT

  以下样例中是10种设计路线的方法:

(1)4-5

(2)1-4-5

(3)4-5,1-2

(4)4-5,1-3

(5)4-5,2-1-3

(6)2-1-4-5

(7)3-1-4-5

(8)1-4

(9)2-1-4

(10)3-1-4

【数据规模和约定】

对于100%的数据,满足1≤N,M≤100000,1≤Q≤120000000。

解题思路: 我好菜啊, 首先想这个是什么题啊? 树DP, 怎么做? 不会啊 ? 好菜好菜。。翻了翻了网上的题解, 磨了快3个小时, 自己终于可以磨出这个DP转移了。。。下面思路来自Orz! 首先m<(n - 1)肯定不连通,先写个特判。

设f[i][j][k]表示以i为根的子树中,最大不便利值为j(到i的最多经过的公路条数),i向儿子连了k条铁路(k=0,1,2)的方案数 然后就是最关键的一步了。j<(log3(n))。这有些类似树链剖分,如果用树链剖分的想法,那么可证j<(log2(n)),其实也已经可以做了。具体可以证明在3叉树时达到上限log3(n),可以自己画图看一下。
然后DP方程及转移就出来了:

设当前点为x,v是x的儿子

令f1=f[v][j-1][0]+f[v][j-1][1]+f[v][j-1][2],f2=f[v][j][0]+f[v][j][1]

f1不向这个儿子建铁路,f2向这个儿子建铁路

f[x][j][2]=f[x][j][2]*f1+f[x][j][1]*f2

f[x][j][1]=f[x][j][1]*f1+f[x][j][0]*f2

f[x][j][0]=f[x][j][0]*f1

然后从小到大,0-log3(n)扫一遍,有方案就输出即可。。

复杂度 O(m * log3n * 3)

总结: 树形DP推不出来方程是自己硬伤,还是多见题,总结套路,说实话到现在树上的分组背包都还有点不会写。

代码如下:

#include <bits/stdc++.h>using namespace std;const int maxn = 100010;const int maxm = 200010;const int maxe = 10;typedef long long LL;int n, m, mod;LL dp[maxn][12][3]; //i的子树,子树内最大不便利值为j,向儿子修的铁路条数k方案数int head[maxn], edgecnt;struct edge{    int v, nxt;}E[maxm];void init(){    memset(head, -1, sizeof(head));    edgecnt = 0;}void addedge(int u, int v){    E[edgecnt].v = v, E[edgecnt].nxt = head[u], head[u] = edgecnt++;}int cal(LL x){    if(x && x % mod == 0) return mod;    return x % mod;}void dfs(int u, int fa){    int cnt = 0;    for(int i = head[u]; ~i; i = E[i].nxt){        int v = E[i].v;        if(v == fa) continue;        dfs(v, u);        cnt++;    }    for(int i = 0; i <= maxe; i++){        dp[u][i][0] = 1;    }    if(cnt == 0) return ;    for(int i = head[u]; ~i; i = E[i].nxt){        int v = E[i].v;        if(v == fa) continue;        for(int j = 0; j <= maxe; j++){            LL t;            LL f1 = !j ? 0 : dp[v][j - 1][0] + dp[v][j - 1][1] + dp[v][j - 1][2];            LL f2 = dp[v][j][0] + dp[v][j][1];//f1不向这个儿子建铁路,f2向这个儿子建铁路            t = (LL) dp[u][j][2] * f1 + (LL)dp[u][j][1] * f2; dp[u][j][2] = cal(t);            t = (LL) dp[u][j][1] * f1 + (LL)dp[u][j][0] * f2; dp[u][j][1] = cal(t);            t = (LL) dp[u][j][0] * f1;  dp[u][j][0] = cal(t);        }    }}int main(){    init();    scanf("%d%d%d", &n, &m, &mod);    for(int i = 1; i <= m; i++){        int u, v;        scanf("%d%d", &u, &v);        addedge(u, v);        addedge(v, u);    }    if(m < n - 1){        puts("-1");        puts("-1");        return 0;    }    dfs(1, 0);    LL ans;    for(int i = 0; i <= maxe; i++){        ans = dp[1][i][0] + dp[1][i][1] + dp[1][i][2];        if(ans){            printf("%d\n", i);            printf("%d\n", (int)ans % mod);            return 0;        }    }    return 0;}
0 0
原创粉丝点击