CF2112E Tree Colorings题解

题目大意,一棵树,有绿,蓝,黄三种颜色,绿蓝之间不能有黄,绿黄之间不能有蓝,绿绿之间也不能有黄绿,根节点是绿色,满足以上条件为一个合法染色,求合法染色数为x时树的最小节点个数。

首先由题意可知,如果一个树当前节点为绿,那么他的子树没有限制,否则子树颜色必须与当前节点颜色相同。

考虑正难则反,求一个树的合法染色数的做法。考虑dp求解,dp[rt]是当前节点为rt且其颜色是绿色时其子树的染色方案数,那么dp转移方程是dp[rt]=(dp[to1]+2)*(dp[to2]+2).....*(dp[tom]+2)  (to1到tom是rt的子节点)

然后考虑本题的做法,我们发现,一个树一定是由比他小的一个树拼上一个子树得到的(因为根节点都是1且1是绿色的)那么我们可以考虑一个dp求解的方法。设f[i]表示合法染色数为i时树的最小节点个数,我们考虑最后

一次子树的拼接,f[i]=f[i/x]+f[x-2] (这个可以参考上面的dp的转移方程)我们发现x必须是i的因数,这时可以去枚举因数来dp,时间复杂度是n根号n的,但是我们可以通过预处理因数的方法使其更快速的实现达到nln(n)

给出代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#define maxn 1000000
#define inf 0x3f3f3f3f3f3f3f3f
#define int long long 
using namespace std;
namespace lin
{
    int read()
    {
        int now=0,f=1;
        char c=getchar();
        while(c<'0'||c>'9')
        {
            if(c=='-') f=-f;
            c=getchar();
        }
        while(c>='0'&&c<='9')
        {
            now=(now<<1)+(now<<3)+(c^48);
            c=getchar();
        }
        return now*f;
    }
    vector<int>v[maxn];
    int f[maxn];
    int love(int wjl)
    {
        for(int i=3;i<=500000;i++)
            for(int j=i;j<=500000;j+=i)
                v[j].push_back(i);
        memset(f,0x3f,sizeof(f));
        f[1]=1;
        for(int i=3;i<=500000;i++)
        {
            int siz=v[i].size();
            for(int j=0;j<siz;j++)
            {
                int x=v[i][j];
                f[i]=min(f[i],f[i/x]+f[x-2]);
            }
        }
        int T=read();
        while(T--)
        {
            int m=read();
            if(f[m]==inf)
                puts("-1");
            else    
                printf("%lld\n",f[m]);
        }
        return wjl;
    }
}
signed main()
{
    return lin::love(0);
}

  

posted @ 2025-06-30 23:20  lntyh  阅读(32)  评论(0)    收藏  举报