CF1211H
Gaochenxi103_QWQ · · 题解
前言
由于 CF 这道题目不支持 C++ 语言的编译,所以如果有 hack 数据或我的代码有问题扣我一下。
做法
首先我们第一部分先计算出最小的最多使用颜色次数。
最大值最小,不难想到二分答案。而二分的目的是确定最多使用次数的上限。在限制了最大使用边数的情况下我们考虑用 DP 来检查当前二分的值的正确性。
因为颜色有
而我们假设节点
而因为与
那么我们定义。
如果为
那么我们怎么在知道
我们将
最后再特判一下
然后是第二部分的输出一种合法方案。
我们可以再用计算出来的答案跑一遍 DP。再 01 背包计算完之后,反推出一种,每个点的父向边是否和自己父亲的父向边一致的数组。计算出这个数组后再将整个树跑一遍根据数组赋值即可。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=3030;
int T,n,mid,dp[N],faf[N],col[N];//子树u内是否有解,包含父向边且子树u内与父向边相同的最小边数,为0表示无解
bool f[N][N];//当前决策层中,考虑到第i个f[v]已经选择了之和为j的元素是否可能
vector<int>G[N];
vector<pair<int,int>>g;
void dfs(int u,int fa)
{
faf[u]=fa;
if(G[u].size()==1 && G[u][0]==fa)
{
dp[u]=1;
return ;
}
vector<int> a(1);
int sum=0;
for(auto v:G[u])
{
if(v==fa)continue;
dfs(v,u);
a.push_back(dp[v]);
sum+=dp[v];
if(dp[v]==0)
{
dp[u]=0;//tang
return ;
}
}
int mx=0;
f[0][0]=1;
for(int i=1;i<=a.size()-1;i++)
{
for(int j=0;j<=mid;j++)
{
f[i][j]=f[i-1][j];
if(j>=a[i])f[i][j]|=f[i-1][j-a[i]];
if(f[i][j])mx=max(mx,j);
}
}
if(u==1)
{
if(sum-mx>mid)dp[u]=0;
else dp[u]=1;
return ;
}
if(sum-mx+1>mid)dp[u]=0;
else dp[u]=sum-mx+1;
}
bool check()
{
memset(dp,0,sizeof dp);
dfs(1,0);
return dp[1];
}
bool ship[N];//与父亲的父向边关系
void calc(int u,int fa)
{
if(G[u].size()==1 && G[u][0]==fa)
{
dp[u]=1;
return ;
}
vector<int> a(1);
vector<int> b(1);
int sum=0;
for(auto v:G[u])
{
if(v==fa)continue;
calc(v,u);
a.push_back(dp[v]);
b.push_back(v);
sum+=dp[v];
}
int mx=0;
f[0][0]=1;
for(int i=1;i<=a.size()-1;i++)
{
for(int j=0;j<=mid;j++)
{
f[i][j]=f[i-1][j];
if(j>=a[i])f[i][j]|=f[i-1][j-a[i]];
if(f[i][j])mx=max(mx,j);
}
}
dp[u]=sum-mx+1;
int now=a.size()-1,val=mx;
// cout<<"=="<<now<<" "<<val<<"==\n";
while(now)
{
if(f[now-1][val])
{
ship[b[now]]=1;
now--;
continue;
}
if(val>=a[now] && f[now-1][val-a[now]])
{
ship[b[now]]=0;
val-=a[now];
now--;
continue;
}
}
}
int cnt=0;
void init(int u,int fa)
{
int r=-1;
for(auto v:G[u])
{
if(v==fa)continue;
if(ship[v])col[v]=col[u];
else
{
if(r==-1)r=++cnt;
col[v]=r;
}
init(v,u);
}
}
void push(int res)
{
memset(dp,0,sizeof dp);
calc(1,0);
// for(int i=1;i<=n;i++)cout<<ship[i]<<"\n";
col[1]=++cnt;
init(1,0);
for(auto v:g)
{
int a=v.first;
int b=v.second;
// cout<<a<<" "<<b<<"\n";
if(faf[b]!=a)
{
cout<<col[a]<<" ";
}
else
{
cout<<col[b]<<" ";
}
}
cout<<"\n";
return ;
}
void solve()
{
cin>>n;
g.clear();
for(int i=1;i<=n;i++)G[i].clear();
for(int i=1;i<n;i++)
{
int u,v;
cin>>u>>v;
g.push_back({u,v});
G[u].push_back(v);
G[v].push_back(u);
}
int l=1,r=n-1,res=n-1;
while(l<=r)
{
mid=(l+r)/2;
if(check())
{
res=mid;
r=mid-1;
}
else l=mid+1;
}
cout<<res<<"\n";
// push(res);
return ;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>T;
while(T--)solve();
return 0;
}