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;
}