描述
可怜最近在玩一款非常有意思的游戏:在游戏中她扮演了一支起义军的首领并在乱世中统一了天下。
现在可怜要给她手下的 K 个功臣分配封地。她选定了 n(n>K)个城市,这 n 个城市被 n−1条双向道路联通,每一条道路的长度都是 1。两个城市i,j 的交通便捷度为 i 到 j 的最短路径长度。
现在可怜要给每一个城市选定一个功臣担任城主。可能有多个城主由同一个人担任,也有可能一个人没有担任任何城主:这些都不是可怜关心的。可怜关心的是,如何巩固她自己的统治:一个功臣统治的城池距离越近,那么他个人的军事力量就会越强大,也越容易威胁到可怜。
定义一个功臣的力量值 wi w i 为他统治的城池两两间交通便捷度的最小值,如果他统治的城池不足 2 个,那么 wi=109 w i = 10 9 。可怜想要分配城主使得 min(wi) m i n ( w i ) 最大,现在她想要知道这个最大值是多少,以及能够达到这个最大值的分配方案有多少个。
输入格式
第一行两个整数 n,K 表示城池数与功臣数。
接下来 n−1 行每行两个数 u,v(1≤u≠v≤n) 表示一条道路。
输出格式
输出一行两个整数表示
min(wi)
m
i
n
(
w
i
)
的最大值以及能达到这个最大值的方案数。方案数可能很大,对 998244353 取模后输出。
样例1
样例输入1
4 2
1 2
2 3
2 4
样例输出1
2 2
样例2
样例输入2
5 3
1 2
1 3
1 4
1 5
样例输出2
2 48
限制与约定
对于 10% 的数据,保证 n≤8
对于 30% 的数据,保证 n≤50
对于 60%的数据,保证 n≤500
对于另外 20% 的数据,保证第 i 条边连接着 i,i+1。
对于 100% 的数据,保证 1≤K
<
<
<script type="math/tex" id="MathJax-Element-762"><</script>n≤2000
时间限制:2s
空间限制:512MB
我们可以发现链的数据比较好做,对于某个点,相当于是限制了一段区间不能有颜色有相同。
这个可以对我们所有数据下的第一问有点启发
我们考虑一个
ans1
a
n
s
1
合法的充要条件
不难发现对于一个合法的
ans1
a
n
s
1
需要满足任取一个点,所有距离这个点小于
ans12
a
n
s
1
2
的点数不得超过
k
k
个
这里我们取的点只有端点和某一条边的中点
因为一条路径的中点只可能落在端点以及边的中点上
我们暂且称端点+中点为关键点
二分的复杂度是不能接受的
但是我们不难发现上面那个判定问题我们可以直接求出每一个关键点满足条件的最大的
wi
w
i
我们只要把距离
i
i
第个近的端点和第
k+1
k
+
1
拉出来讨论即可
第二问就比较好处理了
一个
dfs
d
f
s
就可以解决,对于每个关键点我们把距离他小于
ans1
a
n
s
1
的端点染色
对于两个相邻的关键点,他们有重复部分和独立部分
对于当前染到的点
i
i
,我们可以记重复部分有个,独立部分有
sum2
s
u
m
2
个
那么这里就有
Csum2k−sum1∗sum2!
C
k
−
s
u
m
1
s
u
m
2
∗
s
u
m
2
!
种不同的方案
根据乘法原理我们只要把每个点的上式乘起来即可
#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define rept(i,x) for(int i = linkk[x];i;i = e[i].n)
#define ll long long
const int p = 998244353;
int linkk[4010] , t , n , k;
bool flag[4010];
ll C[2010][2010] , jc[4010];
int ans1 = 2000000000;
ll ans2;
struct node{
int n , y;
}e[8010];
namespace fastIO{
#define BUF_SIZE 100000
#define OUT_SIZE 100000
bool IOerror=0;
inline char nc(){
static char buf[BUF_SIZE],*p1=buf+BUF_SIZE,*pend=buf+BUF_SIZE;
if (p1==pend){
p1=buf; pend=buf+fread(buf,1,BUF_SIZE,stdin);
if (pend==p1){IOerror=1;return -1;}
}
return *p1++;
}
inline bool blank(char ch){return ch==' '||ch=='\n'||ch=='\r'||ch=='\t';}
inline void read(int &x){
bool sign=0; char ch=nc(); x=0;
for (;blank(ch);ch=nc());
if (IOerror)return;
if (ch=='-')sign=1,ch=nc();
for (;ch>='0'&&ch<='9';ch=nc())x=x*10+ch-'0';
if (sign)x=-x;
}
inline void read(ll &x){
bool sign=0; char ch=nc(); x=0;
for (;blank(ch);ch=nc());
if (IOerror)return;
if (ch=='-')sign=1,ch=nc();
for (;ch>='0'&&ch<='9';ch=nc())x=x*10+ch-'0';
if (sign)x=-x;
}
#undef OUT_SIZE
#undef BUF_SIZE
};
using namespace fastIO;
void insert(int x,int y)
{
e[++t].y = y;e[t].n = linkk[x];linkk[x] = t;
e[++t].y = x;e[t].n = linkk[y];linkk[y] = t;
return;
}
void init()
{
read(n);read(k);
rep(i,1,n-1)
{
int x , y;
read(x);read(y);
insert(x , i + n);
insert(i + n , y);
}
C[0][0] = 1;
rep(i,1,n)
{
C[i][0] = C[i][i] = 1;
rep(j,1,i-1) C[i][j] = (C[i-1][j] + C[i-1][j-1])%p;
}
jc[0] = 1;
rep(i,1,2*n) jc[i] = i * jc[i-1] % p;
return;
}
queue<int>q;
int dep[4010] , tmp[4010] , o;
bool vis[4010];
void bfs(int x)
{
o = 0;
memset(vis,0,sizeof(vis));
dep[x] = 0;vis[x] = true;q.push(x);
while(!q.empty())
{
int u = q.front();if(u <= n)tmp[++o] = u;
for(int i = linkk[u];i;i = e[i].n)
if(!vis[e[i].y])
{
vis[e[i].y] = true;
dep[e[i].y] = dep[u] + 1;
q.push(e[i].y);
}
q.pop();
}
return;
}
void dfs(int x,int fa)
{
bfs(x);
int sum1 = 0, sum2 = 0;//sum1为重合部分,sum2为单独部分
for(int i = 1;dep[tmp[i]] < ans1 && i <= o;++i)
if(flag[tmp[i]]) sum1++;
else sum2++ , flag[tmp[i]] = true;
ans2 = (ans2 * C[k-sum1][sum2] % p) * jc[sum2] %p;
rept(i,x) if(e[i].y != fa) dfs(e[i].y , x);
return;
}
int main()
{
init();
bfs(1);
rep(i,1,2*n) bfs(i),ans1 = min(ans1 , dep[tmp[k]] == dep[tmp[k+1]]? dep[tmp[k]] : dep[tmp[k]]+1);
ans2 = 1;
dfs(1,0);
printf("%d %lld\n",ans1,ans2);
return 0;
}