[CF1481F]AB Tree

AB Tree

题解

按题解说挺套路的

记最深的点的深度为 d d d,那么答案要么是 d d d,要么是 d + 1 d+1 d+1
下界是 d d d很容易看出,由于不同长度的字符串都有 d d d个,所以下界最低是 d d d
而对于 d + 1 d+1 d+1的上界,我们可以这样考虑。
首先我们先将所有的点分成叶子节点与非叶子节点两类。
由于每个非叶节点至少有一个儿子,所以非叶节点的个数不会超过 ⌊ n 2 ⌋ \left\lfloor\frac{n}{2}\right\rfloor 2n
所以对于所有非叶节点,必然存在一种方案,使得同一深度的非叶节点都是同一颜色。
于是我们对某一深度的非叶节点先染成同一颜色,而对于这一层的叶节点,如果能都染成与非叶节点相同的的颜色,就染成与其相同的颜色,否则先将这一颜色染完,再染其它颜色。
由于所有层最多在某一层叶节点出现一次不同颜色,所以上界是 d + 1 d+1 d+1

对于取到下界的答案,必然是将每一层层全部染成同种颜色。
对于这个,我们很容易想到一种 O ( n 2 ) O\left(n^2\right) O(n2)的背包 d p dp dp做法,但这明显过不了。
但由于点数不同的层数的不会超过 n \sqrt{n} n ,所以我们相当于做的是一个多重背包。
通过二进制拆分,我们实际上的时间复杂度 O ( n n ) O\left(n\sqrt{n}\right) O(nn )
我们只需要对于背包记录下路径即可。
如果可行就按路径染色,否则就按上面 d + 1 d+1 d+1的染色方法对其进行染色。

时间复杂度 O ( n n ) O\left(n\sqrt{n}\right) O(nn )

源码

不过 500 500 500的背包加入元素数好像过不了,要开 600 600 600

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100005
#define lowbit(x) (x&-x)
#define reg register
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
const int INF=0x3f3f3f3f;
const int mo=1e9+7;
const int iv2=5e8+4;
const int lim=1000000;
const int jzm=2333;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
typedef pair<int,int> pii;
const double PI=acos(-1.0);
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int add(int x,int y){return x+y<mo?x+y:x+y-mo;}
int n,x,t1,t2,head[MAXN],h[MAXN],dep[MAXN],tot,idx,tsiz[MAXN];
bool dp[605][MAXN],pre[605][MAXN],ans[MAXN];
struct edge{int to,nxt;}e[MAXN<<1];
struct ming{vector<int>A;int siz;}a[605];
vector<int>vec[MAXN],bl[MAXN];
void addEdge(int u,int v){e[++tot]=(edge){v,head[u]};head[u]=tot;}
void dosaka(int u,int fa){
	dep[u]=dep[fa]+1;vec[dep[u]].push_back(u);h[u]=tsiz[u]=1;
	for(int i=head[u];i;i=e[i].nxt)
		if(e[i].to!=fa)dosaka(e[i].to,u),
		h[u]=max(h[e[i].to]+1,h[u]),tsiz[u]+=tsiz[e[i].to];
} 
signed main(){
	read(n);read(x);t1=x;t2=n-x;
	for(int i=2;i<=n;i++){int p;read(p);addEdge(p,i);}dosaka(1,0);
	for(int i=1;i<=n;i++)bl[(int)vec[i].size()].push_back(i);
	for(int i=1;i<=n;i++){
		int siz=bl[i].size(),id=0;if(!siz)continue;
		for(int j=1;id+j<=siz;j+=j){
			idx++;a[idx].siz=j*i;
			for(int k=id;k<id+j;k++)
				a[idx].A.push_back(bl[i][k]);
			id+=j;
		}
		if(id==siz)continue;idx++;a[idx].siz=(siz-id)*i; 
		for(int k=id;k<siz;k++)
			a[idx].A.push_back(bl[i][k]);
	}
	dp[0][0]=1;
	for(int i=1;i<=idx;i++){
		for(int j=n;j>=a[i].siz;j--)
			if(dp[i-1][j-a[i].siz])dp[i][j]=pre[i][j]=1;
		for(int j=0;j<=n;j++)if(dp[i-1][j])dp[i][j]=1;	
	}
	if(dp[idx][t1]){
		printf("%d\n",h[1]);int now=t1;
		for(int i=idx;i>0;i--){
			int siz=a[i].A.size();
			for(int j=0;j<siz;j++){
				int u=a[i].A[j],sz=vec[u].size();
				for(int k=0;k<sz;k++)ans[vec[u][k]]=pre[i][now];
			}
			if(pre[i][now])now-=a[i].siz;
		}
		for(int i=1;i<=n;i++)if(ans[i])putchar('a');else putchar('b');puts("");
	}
	else{
		printf("%d\n",h[1]+1);
		for(int i=1;i<=n;i++){
			int sum=0,siz=vec[i].size();
			for(int j=0;j<siz;j++)if(tsiz[vec[i][j]]!=1)sum++;
			if(t1>=sum){
				for(int j=0;j<siz;j++)
					if(tsiz[vec[i][j]]!=1)ans[vec[i][j]]=1;
				t1-=sum;
				for(int j=0;j<siz;j++){
					if(tsiz[vec[i][j]]!=1)continue;
					if(t1)ans[vec[i][j]]=1,t1--;else t2--;
				}
			}
			else{
				t2-=sum;
				for(int j=0;j<siz;j++){
					if(tsiz[vec[i][j]]!=1)continue;
					if(t2)t2--;else ans[vec[i][j]]=1,t1--;
				}
			}
		}
		for(int i=1;i<=n;i++)if(ans[i])putchar('a');else putchar('b');puts("");
	}
	return 0;
}

谢谢!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值