构造题笔记

CF1098C Construct a tree
套用某题解的一句话:\(\color{Blue}{Rating 2300以下送命题}\)
整体思路其实很简单。
先考虑每一个节点对子树大小的贡献,为其深度。
我们二分一个 \(k\) , 线性算出满 \(k\) 叉树的子树大小和,求出使和和 \(\le s\) 的最大 \(k\)
然后考虑将部分节点下移,以达到加大子树大小和的目的。
\(Code:\)

#include <bits/stdc++.h>
#define int long long
#define rgi register int
using namespace std;
const int M=1e5+7,inf=1e9+7;
inline int read(){
	int w=0,r=1;char c=getchar();
	while(!(isdigit(c)||c=='-'))c=getchar();
	if(c=='-')r=-1,c=getchar();
	while(isdigit(c))w=w*10+c-'0',c=getchar();
	return w*r;
}
int n,s,dep[M],cnt[M];
bool gva(int k){
	int ret=s,nn=1,i=1,depp=1;
	memset(cnt,0,sizeof(cnt));
	for(;nn<=n;i*=k,depp++){
		int x=min(n-nn+1,i);
		cnt[depp]=x;
		for(int j=1;j<=x;j++)dep[nn+j-1]=depp;
		nn+=x;
		ret-=x*depp;
	}
//	printf("%lld\n",ret);
	if(ret<0)return 0;
	nn=n;
	while(ret>0){
		if(cnt[dep[nn]]==1)--nn;
		i=min(ret,depp-dep[nn]);
		cnt[dep[nn]]--;
		dep[nn]+=i;
		cnt[dep[nn]]++;
		ret-=i;
		--nn;
		++depp;
	}
	return 1;
}
signed main(){
	n=read(),s=read();
	if(s<2*n-1||s>n*(n+1)/2){
		printf("No\n");
		return 0;
	}
	printf("Yes\n");
	int l=1,r=n-1,mid;
	while(l<r){
		mid=(l+r)/2;
//		printf("%lld %lld  %lld\n",l,r,mid);
		int val=gva(mid);
//		for(int i=1;i<=10;i++)printf("cnt[%lld]=%lld\n",i,cnt[i]);
		if(!val)l=mid+1;
		else r=mid;
	}
//	printf("%lld %lld  %lld\n",l,r,mid);
	gva(l);
	int tot=1;
	sort(dep+2,dep+n+1);
///	for(int i=1;i<=10;i++)printf("cnt[%lld]=%lld\n",i,cnt[i]);
	memset(cnt,0,sizeof(cnt));
	for(int i=2;i<=n;i++){
		while(dep[tot]!=dep[i]-1||cnt[tot]==l)tot++;
		cnt[tot]++;
		printf("%lld ",tot);
	}
	return 0;
}
/*
4 42

6 15

32 98
*/

CF750F New Year and Finding Roots

 Debug 是最寻常的,一调就是三两天。可别恼。

在用户ZHua_Lun_的帮助下,我用了五天才把这道题改出来。
我现在深刻认识到了,写题与Debug时有一个清晰的思路与沉着的心态是多么的重要。


回归正题。
题目内容:

  • 告诉你一棵深度为 \(h(h\le7)\) 的满二叉树,节点随机编号为 \(1\sim 2^h-1\),编号不重复 。
  • 你可以询问每一个节点连向哪几个节点,要求在 \(16\) 次询问内找出根节点。

刚看题目时,谁都觉得 \(16\) 个询问很难找出根节点。千万不要被吓倒。
考虑分析该二叉树中每个节点的性质。

  • 1.只有一个节点与其相连,为叶节点;
  • 2.有两个节点与其相连,为根节点,碰见直接输出答案;
  • 3.普通的节点,有三个节点与其相连。

在这些节点中,叶节点是我们尤其需要利用的点。靠它们,我们才能让初始点不断找到自己的父亲,最终确认根节点。
我们随机从一个点(设为 \(st\))开始。
为保证每个被访问过的节点得到充分的利用,我们采用如下策略:

  • \(st\) 为叶节点时,直接向上跳即可;
  • \(st\) 未求出深度,向三个方向进行 \(bfs\) 访问,若已求出深度则向两个未访问的方向访问;
  • 访问的过程中,对于每个不为 \(st\) 的点,向一个未访问的方向访问(往更多方向延伸是没有必要的);
  • \(bfs\) 探索到叶节点时停止搜索,用已知信息求得 \(st\) 的深度与父节点,向上跳。
  • \(st\) 的深度 \(\le3\) 时特判。

image

(如图,为二叉树深度为 \(7\)\(st\) 深度为 \(4\) 时的 \(bfs\) 图)

使 \(st\) 跳跃到父节点直接所需的询问个数为\(数的深度-st的深度+1\)
所以在最坏情况下(即 \(st\) 的深度为 \(6\)\(7\) 时)询问个数为 \(1+2+3+4+1+2+4=17\) 次。
还需要一个小优化:\(dep[st]==3\) 时,询问剩余 \(7\) 个点中的 \(6\) 个,若都不为根节点,则输出剩余的一个节点。
该思路即为正解。若不懂可以去看这篇博客,比我写得详细。
\(Code\)未挖坑):

#include <bits/stdc++.h>
#define ll long long
#define rgi register int
using namespace std;
const int M=128;
inline int read(){
	int w=0,r=1;char c=getchar();
	while(!(isdigit(c)||c=='-'))c=getchar();
	if(c=='-')r=-1,c=getchar();
	while(isdigit(c))w=w*10+c-'0',c=getchar();
	return w*r;
}
int fa[M],dep[M],edge[M][3],went[M],mm[M];
int dd,n,u[3],s[3];

int tedge[M][3],tmm[M];
void askk(int x,int ff){
	if(went[x])return;
	went[x]=1;
	fa[x]=ff;
	printf("? %d\n",x);
	fflush(stdout);
	scanf("%d",&mm[x]);
//	mm[x]=tmm[x];
	for(int i=0;i<mm[x];i++)scanf("%d",&edge[x][i]);
//	for(int i=0;i<mm[x];i++)edge[x][i]=tedge[x][i];
}
int find1(int x,int ff){
	askk(x,ff);
	for(int i=0;i<3;i++)
		if(fa[x]!=edge[x][i])return edge[x][i];
	return edge[x][2];
}
int find2(int x,int ff){
	askk(x,ff);
	for(int i=2;i>=0;i--)
		if(fa[x]!=edge[x][i])return edge[x][i];
	return edge[x][0];
}
int bfs(int st){
//	printf("quedin   %d\n",st);
	if(mm[st]==2)return st;
	else if(mm[st]==1){
		if(!dep[st])dep[st]=dd;
		fa[st]=edge[st][0],askk(fa[st],st);
		dep[fa[st]]=dep[st]-1;
		return bfs(fa[st]);
	}else{
		if(!dep[st]){
			for(int i=0;i<3;i++)u[i]=st,s[i]=edge[st][i];
			dep[st]=dd;
			while(mm[u[0]]!=1&&mm[u[1]]!=1&&mm[u[2]]!=1){
//				printf("%d %d %d    %d %d %d\n",u[0],u[1],u[2],mm[u[0]],mm[u[1]],mm[u[2]]);
				for(int i=0;i<3;i++){
					int stp=s[i];
					s[i]=find1(s[i],u[i]);
					u[i]=stp;
					if(mm[u[i]]==2)return u[i];
				}
				dep[st]--;
			}
			for(int i=0;i<3;i++)
				if(mm[u[i]]==3){
					fa[st]=edge[st][i];
					break;
				}
			dep[fa[st]]=dep[st]-1;
			return bfs(fa[st]);
		}
		else if(dep[st]==2){
			int a1=find1(st,fa[st]),a2=find2(st,fa[st]);
			askk(a1,st);
			if(mm[a1]==2)return a1;
			else return a2;
		}
		else if(dep[st]==3){
			int a1=find1(st,fa[st]),a2=find2(st,fa[st]);
			int b1=find1(a1,st),b2=find2(a1,st),b3=find1(a2,st),b4=find2(a2,st);
			askk(b1,a1),askk(b2,a1),askk(b3,a2);
			if(mm[b1]==2)return b1;
			else if(mm[b2]==2)return b2;
			else if(mm[b3]==2)return b3;
			else return b4;
		}
		else{
			for(int i=0;i<2;i++)u[i]=st;
			s[0]=find1(st,fa[st]),s[1]=find2(st,fa[st]);
			for(int j=1;j<=dd-dep[st];j++){
//				printf("2\n");
				for(int i=0;i<2;i++){
					int stp=s[i];
					s[i]=find1(s[i],u[i]);
					u[i]=stp;
					if(mm[u[i]]==2)return u[i];
				}
			}
			if(mm[u[0]]==3)fa[st]=find1(st,fa[st]);
			else fa[st]=find2(st,fa[st]);
			dep[fa[st]]=dep[st]-1;
			return bfs(fa[st]);
		}
	}
}
signed main(){
	int t;
	scanf("%d",&t);
	while(t--){
		memset(fa,0,sizeof(fa));
		memset(dep,0,sizeof(dep));
		memset(edge,0,sizeof(edge));
		memset(went,0,sizeof(went));
		memset(mm,0,sizeof(mm));
		dd=read();
		n=(1<<dd)-1;
		/*for(int i=1;i<n;i++){
			int fr=read(),tr=read();
			tedge[fr][tmm[fr]++]=tr,tedge[tr][tmm[tr]++]=fr;
		}
		for(int i=1;i<=n;i++){
			printf("***%d %d      ",i,tmm[i]);
			for(int j=0;j<tmm[i];j++)printf("%d ",tedge[i][j]);
			printf("\n");
		}*/
		if(dd<=4){
			for(int i=1;i<=n;i++){
				printf("? %d\n",i);
				fflush(stdout);
				int x,y;
				scanf("%d",&x);
				for(int j=1;j<=x;j++)scanf("%d",&y);
				if(x==2){
					printf("! %d\n",i);
					fflush(stdout);
					break;
				}
			}
		}
		else{
			askk(1,0);
			printf("! %d\n",bfs(1));
			fflush(stdout);
		}
	} 
	return 0;
}
/*
15
6
39 60
39 59
60 36
60 42
59 52
59 18
36 54
36 28
42 51
42 47
52 1
52 27
18 14
18 13
54 23
54 21
28 25
28 20
51 2
51 16
47 17
47 6
1 61
1 3
27 31
27 55
14 4
14 41
13 24
13 32
23 63
23 12
21 11
21 15
25 35
25 38
20 48
20 33
2 50
2 22
16 40
16 29
17 7
17 45
6 26
6 43
61 8
61 53
3 5
3 10
31 46
31 58
55 19
55 44
4 34
4 57
41 30
41 56
24 49
24 37
32 62
32 9
*/
posted @ 2021-09-15 09:47  wcy2006  阅读(49)  评论(0)    收藏  举报