AtCoder Grand Contest 024 简要题解

链接

D:
首先观察到颜色数等于树上点的种类数。找出最长链,设长度为D,那么至少有[(D+1)/2]种。
通过把所有点都补成对称形式,这个下界也是可以达到的。

下面考虑叶子个数。
若D为偶数,显然叶子的每一层度数最大的节点(度数-1)的乘积

若D为奇数,则可以枚举最长链增长1(唯一中心点 --> 两个中心点),叶子个数每种情况取min

注意代码细节,只能更专注的写才能不调试

#include<bits/stdc++.h>
using namespace std;
#define maxn 120
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define rvc(i,S) for(register int i = 0 ; i < (int)S.size() ; i++)
#define rvcd(i,S) for(register int i = ((int)S.size()) - 1 ; i >= 0 ; i--)
#define fore(i,x) for(register int i = head[x] ; i ; i = e[i].next)
#define pb push_back
#define prev prev_
#define stack stack_
#define inf 0x3f3f3f3f

typedef unsigned long long ll;

struct node{
	int next,to;
}e[maxn * 2];
int head[maxn],cnt;
int n,dth[maxn],fa[maxn],deg[maxn];
int mx[maxn];
int col;
ll num = 1e18,sum[maxn];

inline void adde(int x,int y){
	e[++cnt].to = y;
	e[cnt].next = head[x];
	head[x] = cnt;
}
void dfs(int x){
	fore(i,x){
		if ( e[i].to == fa[x] ) continue;
		fa[e[i].to] = x , dth[e[i].to] = dth[x] + 1;
		dfs(e[i].to);
	}
}

void dfs(int x,int fa){
	fore(i,x){
		if ( e[i].to == fa ) continue;
		dth[e[i].to] = dth[x] + 1;
		dfs(e[i].to,x);
	}
}
bool legal(int x,int fa){
	return 1;
}
void init(){
	dfs(1);
	int rt = 0,x = 0;
	rep(i,1,n) if ( dth[rt] < dth[i] ) rt = i;
	rep(i,1,n) fa[i] = dth[i] = 0;
	dth[rt] = 1 , dfs(rt);
	rep(i,1,n) if ( dth[x] < dth[i] ) x = i;
	col = (dth[x] + 1) / 2;
	if ( dth[x] & 1 ){
		rep(i,1,col - 1) x = fa[x];
		dth[x] = 0 , dfs(x,0);
		rep(i,1,n) mx[dth[i]] = max(mx[dth[i]],deg[i] - 1);
		sum[col - 1] = 1;
		repd(i,col - 2,1){
			sum[i] = sum[i + 1] * mx[i];
		}
		num = sum[1] * deg[x];
		fore(i,x){
			if ( legal(e[i].to,x) ){
				int mid1 = x , mid2 = e[i].to;
				dth[mid1] = dth[mid2] = 0 , dfs(mid1,mid2) , dfs(mid2,mid1);
				rep(j,0,n) sum[j] = 0 , mx[j] = 0;
				rep(j,1,n) mx[dth[j]] = max(mx[dth[j]],deg[j] - 1);
				sum[col - 1] = 1;
				repd(j,col - 2,0){
					sum[j] = sum[j + 1] * mx[j];
				}
				num = min(num,2ll * sum[0]);
			}
		}
	}
	else{
		int mid1,mid2;
		rep(i,1,col - 1) x = fa[x];
		mid1 = x , mid2 = fa[x];
		dth[mid1] = dth[mid2] = 0 , dfs(mid1,mid2) , dfs(mid2,mid1);
		rep(i,1,n) mx[dth[i]] = max(mx[dth[i]],deg[i] - 1);
		sum[col - 1] = 1;
		repd(i,col - 2,0){
			sum[i] = sum[i + 1] * mx[i];
		}
		num = 2ll * sum[0];
	}
}
int main(){
	freopen("input.txt","r",stdin);
	scanf("%d",&n);
	rep(i,1,n - 1){
		int x,y;
		scanf("%d %d",&x,&y);
		adde(x,y) , adde(y,x);
		deg[x]++ , deg[y]++;
	}
	init();
	cout<<col<<" "<<num<<endl;
}

E:

首先观察一个结论:
因为字典序最大,所以插入的数字x >= y
若x==y,则可以在y后第一个x>r的地方插入,所得到序列完全相同。
因此,只考虑插入x > y的情况,就可以不重不漏的得到序列

把不断插入元素得到序列的过程看成在树上加入叶子得到一颗树的过程
每个节点维护两个值:id 插入顺序,val 填的元素
则该树满足

  1. 任何节点的id和val严格大于pnt
  2. 共n+1个节点,id为0-n的排列
  3. 1的父亲必须是0 意味着只需要枚举1号节点子树大小即可
    于是可以利用第三条性质简单的dp了
    f[i][j]表示i个点,根填的是j
    f[i][j] = C(i - 2,k - 1) * f[k][y] ( y > j) * f[i - k][j] (其他节点仍然以0号点为根)
#include<bits/stdc++.h>
using namespace std;
#define maxn 320
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define rvc(i,S) for(register int i = 0 ; i < (int)S.size() ; i++)
#define rvcd(i,S) for(register int i = ((int)S.size()) - 1 ; i >= 0 ; i--)
#define fore(i,x) for(register int i = head[x] ; i ; i = e[i].next)
#define pb push_back
#define prev prev_
#define stack stack_
#define inf 0x3f3f3f3f

typedef unsigned long long ll;

int n,k,mod;
ll f[maxn][maxn],sum[maxn][maxn];
ll c[maxn][maxn];

inline void up(ll &x,ll y) { x = (x + y) % mod; }
void init(){
	c[0][0] = 1;
	rep(i,1,n + 1){
		c[i][0] = 1;
		rep(j,1,i){
			c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;
		}
	}
}
int main(){
	cin>>n>>k>>mod;
	init();
	repd(i,k,0) f[1][i] = 1 , sum[1][i] = (sum[1][i + 1] + f[1][i]) % mod;
	rep(i,2,n + 1){
		rep(j,0,k){
			rep(l,1,i - 1){
				up(f[i][j],c[i - 2][l - 1] * sum[l][j + 1] % mod * f[i - l][j] % mod);
			}
		}
		repd(j,k,0) up(sum[i][j],sum[i][j + 1] + f[i][j]);
	}
	cout<<f[n + 1][0]<<endl;
}

猜你喜欢

转载自blog.csdn.net/weixin_42484877/article/details/84798854