CF1943C-构造、树直径
link:https://codeforces.com/contest/1943/problem/C
题意:给一棵树,初始所有点为白色,每次操作可以选一个点 \(v\),和一个距离 \(d\),表示将所有距离点 \(v\) 恰好 \(d\) 的点染成黑色,问最少需要几次操作让树全黑,给出操作序列。
树、二分图、黑白染色
一条链怎么做? \(s_1,\dots,s_n\),如果 \(n\) 是奇数对着中间做 \(\frac{n+1}{2}\) 次操作,这是下确界(因为每次至多染2个点);如果 \(n\) 是偶数,意味着有两个相邻中心,手玩一下会发现 \(n\bmod 4=2\) 和 \(n\bmod 4=0\) 是两种情形,如果 \(n\bmod 4=0\) ,可以先对着 \(\frac{n}{2}\) 为中心一直染色,最后剩下 \(\frac{n}{2},n\) 两个点没染,他们的中心点 \(\frac{3}{4}n\) 恰好是一个点,此时是 \(n/2\) 次操作,而如果 \(n\bmod 4=2\) 则需要额外的一次。
对于一棵树来说,直径必然是其下界,也是下确界。奇数的情况不会变,而如果 \(n\) 是偶数,两个中心就需要做一个选择。
这里一个比较有启发性的想法就是去考虑黑白染色:因为相邻的中心必然是不同色的,黑色中心只去染白色点,白色中心只去染黑色点,两个方案互相独立,因此对两个中心各自dfs一次,计算最大深度即可知道答案:
(记得清空数组…因为这个wa了一段时间…)
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define endl '\n'
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef pair<int,int> pii;
const int N=2e3+5;
int n,fa[N],dep[N],c;
vector<vector<int>> G;
void dfs(int x){
for(auto to:G[x])if(to!=fa[x]){
fa[to]=x;
dep[to]=dep[x]+1;
if(dep[to]>dep[c])c=to;
dfs(to);
}
}
int main(){
fastio;
int tc;cin>>tc;
while(tc--){
cin>>n;
G=vector<vector<int>>(n+1);
rep(i,1,n-1){
int u,v;
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
rep(i,1,n)fa[i]=dep[i]=0;
c=1;dfs(1);
int L=c,R;
rep(i,1,n)fa[i]=dep[i]=0;
dfs(c);
R=c;
vector<int> p;
p.push_back(-1);
while(L!=R){
p.push_back(R);
R=fa[R];
}
p.push_back(L);
int sz=p.size()-1;
vector<pii> ans;
if(sz&1){
rep(i,0,sz/2)ans.push_back({p[sz/2+1],i});
}else{
int c1=p[sz/2],c2=p[sz/2+1];
rep(i,1,n)fa[i]=dep[i]=0;
dfs(c1);
int mx=0;
rep(i,1,n)mx=max(mx,dep[i]);
for(int i=1;i<=mx;i+=2)ans.push_back({c1,i});
rep(i,1,n)fa[i]=dep[i]=0;
dfs(c2);
mx=0;
rep(i,1,n)mx=max(mx,dep[i]);
for(int i=1;i<=mx;i+=2)ans.push_back({c2,i});
}
cout<<ans.size()<<endl;
for(auto [x,d]:ans)cout<<x<<' '<<d<<endl;
}
return 0;
}