题目传送门
题目大意:
给定一棵树,你可以在上面指定一些不相交的链,试问从每个节点到根经过的非链边最多的最小值是多少。
思考过程&具体做法:
看别人的题解艰难地写出来的。
首先impossible很好判断,图不联通直接输出就行了。
图联通的话,
我们对于每一个点,在所有向下的路径中选择最长的两条,然后把这两条全部变成铁路。那么假设f(x)为有x节点的树的最大可能答案,那么就有f(x)<=f(x/3)+1,因此得到f(N)<=log3N,当N=10^5时有10 < log3N < 11,那么可以知道答案最大为10。(摘自别人的博客)
这样我们就可以枚举答案了,令dp[i][j][0/1/2]表示对于一个点i,向下建了0/1/2条边,子树中不便利值不超过j的方案数,如何转移请看代码。
代码:
#include <bits/stdc++.h>
using namespace std;
const long long maxn=1e5+100;
struct stu
{
long long to,next;
}road[2*maxn]; long long first[maxn],cnt=0;
long long mod,n,m;
long long dp[maxn][12][3];
void addedge(long long x,long long y)
{
road[++cnt].to=y;
road[cnt].next=first[x];
first[x]=cnt;
}
long long modd(long long x)//要特判%mod后为0的情况
{
if(!x) return 0;
long long t=x%mod;
return t?t:mod;
}
void dfs(long long x,long long k,long long last)
{
long long p,t1,t2; dp[x][k][0]=1;
for(long long i=first[x];i;i=road[i].next)
{
long long to=road[i].to;
if(to==last) continue;
dfs(to,k,x);
t1=k?modd(dp[to][k-1][0]+dp[to][k-1][1]+dp[to][k-1][2]):0;//一个点可以不向他的儿子连边
t2=modd(dp[to][k][0]+dp[to][k][1]);//也可以向他的儿子连边
dp[x][k][2]=modd(dp[x][k][2]*t1+dp[x][k][1]*t2);
dp[x][k][1]=modd(dp[x][k][1]*t1+dp[x][k][0]*t2);
dp[x][k][0]=modd(dp[x][k][0]*t1);//为了保证无后效性,必须以2,1,0的顺序转移
}
}
int main()
{
scanf("%lld%lld%lld",&n,&m,&mod);
for(long long i=1;i<=m;i++)
{
long long x,y;
scanf("%lld%lld",&x,&y);
addedge(x,y);addedge(y,x);
}
if(m<n-1) { printf("-1\n-1");return 0; }
for(long long i=0; ;i++)
{
dfs(1,i,0);
if(dp[1][i][0]||dp[1][i][1]||dp[1][i][2])
{
printf("%lld\n%lld",i,(dp[1][i][0]+dp[1][i][1]+dp[1][i][2])%mod);
return 0;
}
}
return 0;
}