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

浙公网安备 33010602011771号