洛谷P4201 [NOI2008]设计路线——题解

题目传送门
题目大意:
给定一棵树,你可以在上面指定一些不相交的链,试问从每个节点到根经过的非链边最多的最小值是多少。


思考过程&具体做法:
看别人的题解艰难地写出来的。
首先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;   
}

猜你喜欢

转载自blog.csdn.net/qq_39662197/article/details/80176636