传送门:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1063
思路:首先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)扫一遍,有方案就输出即可
#include<cstdio>
#include<cstring>
#include<algorithm>
const int maxn=100010,maxm=200010,lim=10;
typedef long long ll;
using namespace std;
int pre[maxm],now[maxn],son[maxm],tot;
int n,m,Q;ll f[maxn][12][3];//i的子树,子树内最大不便利值为j,向儿子修的铁路条数k方案数。
void add(int a,int b){pre[++tot]=now[a],now[a]=tot,son[tot]=b;}
int get(ll t){return !t?0:t%Q?t%Q:Q;}//如果方案数%Q==0就设为Q,不能直接设为0,否则会被当成没有方案
void dfs(int x,int fa){
int cnt=0;
for (int y=now[x];y;y=pre[y]) if (son[y]!=fa) dfs(son[y],x),cnt++;
for (int i=0;i<=lim;i++) f[x][i][0]=1;
if (!cnt) return;
for (int y=now[x];y;y=pre[y]) if (son[y]!=fa){
int v=son[y];
for (int j=0;j<=lim;j++){
ll t,f1=!j?0: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向这个儿子建铁路
t=(ll)f[x][j][2]*f1+(ll)f[x][j][1]*f2;f[x][j][2]=get(t);
t=(ll)f[x][j][1]*f1+(ll)f[x][j][0]*f2;f[x][j][1]=get(t);
t=(ll)f[x][j][0]*f1;f[x][j][0]=get(t);
}
}
}
int main(){
scanf("%d%d%d",&n,&m,&Q);
for (int i=1,a,b;i<=m;i++) scanf("%d%d",&a,&b),add(a,b),add(b,a);
if (m<n-1){puts("-1\n-1");return 0;}
dfs(1,0);ll sum;
for (int i=0;i<=lim;i++) if (sum=f[1][i][0]+f[1][i][1]+f[1][i][2]) return printf("%d\n%d\n",i,(int)sum%Q),0;
return 0;
}