AtCoder Regular Contest 095


C - Many Medians

题目链接

题意:给n个数(n为偶数),然后问除第i个数以后的(n-1)个数的第(n-1)/2大是多少,输出所有的i.

思路:对原数组copy一份numa[],排序,找出中间的第n/2大和第(n/2)+1大;对于原数组任意一个numb[i],如果numb[i]<=numa[n/2],则剔除numb[i]后,数组numa[]的中间那个值,即第(n-1)/2大的值会向右移一位;如果numb[i]>numa[n/2],则剔除numb[i]后,数组numa[]的中间那个值,即为第(n-1)/2大的值。

#include<bits/stdc++.h>
using namespace std;
const int maxn=200000+10;
int numa[maxn];
int numb[maxn];
int main() {
	int n;
	while(~scanf("%d",&n)) {
		for(int i=1; i<=n; i++) {
			scanf("%d",&numa[i]);
			numb[i]=numa[i];
		}
		sort(numa+1,numa+n+1);
		int m=n/2;
		for(int i=1; i<=n; i++) {
			if(numb[i]<=numa[m])
				printf("%d\n",numa[m+1]);
			else
				printf("%d\n",numa[m]);
		}
	}
	return 0;
}

D - Binomial Coefficients

题目链接

题意:给出n个数,求这n个数中任取两个数a和b,求能使Comb(a,b)最大的a和b;

思路:简单结论题,通过推理可以得知,对于n个数中的最大值num[m],必定存在一个数num[k],使得num[k]最接近num[m]/2这个值,则Comb(num[m],num[k])>=Comb(num[m-1],num[k])>=Comb(num[m-1],num[k-1]),故Comb(num[m],num[k])为最大值,求出num[m]和num[k]即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn=200000+10;
int num[maxn];
int cmd(int a,int b) {
	return a>b;
}
int main() {
	int n;
	while(~scanf("%d",&n)) {
		for(int i=0; i<n; i++)
			scanf("%d",&num[i]);
		sort(num,num+n);
		int m=num[n-1];
		int posa=lower_bound(num,num+n,m/2)-num;
		int a=num[posa];   //从右边最逼近m/2的值 
		sort(num,num+n,cmd);
		int posb=lower_bound(num,num+n,m/2,greater<int>())-num;
		int b=num[posb];   //从左边最逼近m/2的值 
		int ans;
		if((m-a)>b)        //算出哪个逼近的值最优 
			ans=a;
		else
			ans=b;
		printf("%d %d\n",m,ans);
	}
	return 0;
}

E - Symmetric Grid

题目链接

题意:给一个n行m列的矩阵,求是否可以通过行和列的转换变成一个对称矩阵。

思路:swaped[]数组记录配对情况,比如记录swaped[i]=j,swaped[j]=i,意思是第i行和第j行是关于中间n/2行对称的(这是假设的),然后通过dfs枚举出所有的关于行的对称情况(当n为奇数时,有一行关于自己对称,可以理解为这恰好为对称矩阵的第(n+1)/2行)因为对称关系是两两配对的,所以第step==(n+1)/2时,刚好实现所有n行的配对情况,我暂时称为这是一种(n行配对)情况,需要判断它的所有列是否能通过转移达到对称。
这里通过一个check()函数实现,对于一种(n行配对)情况--这种情况是后面列配对的前提,通过对列进行配对,配对的条件是所有的第i列的k行的内容和所有的第j列的swaped[k]行的内容对应相等,如果可以,说明可以通过转移列,使它们在对称的位置。然后计算所有没配对的列,结果为cnt。如果cnt>=2或者列数m为偶数,但cnt==1,即有一列没配对,这些都说明在这种(n行配对)的情况下,无法通过列的转移实现矩阵的对称。如果刚好cnt==1&&列数m为奇数,这说明这一列刚好是这矩阵的中间一列,需要判断这一列是否在(n行配对)的情况下对称。如果也对称了,说明这个矩阵在(n行配对)的情况下相应
转移行,再相应的转换列,便可以实现对称矩阵。

举个栗子,

4 5
rrdca
adrcr
brdgt

tdrgb

(答案:YES)

在swaped[0]=1,swaped[1]=0,swaped[2]=3,swaped[3]=2的n行配对情况下,即第0行和第1行

是对称的,第2行和第3行是对称的。第1列可以和第2列相应配对(转换),说明第2列和第3列可以通过

转移到对称的位置上。同理,第1列和第5列相应配对,剩下第3列没配对,进行特判。

#include<bits/stdc++.h>
using namespace std;
const int maxn=20+10;
const int INF=int(1e9)+10;
#define LL long long
char str[maxn][maxn];
int swaped[maxn];
bool used[maxn];
bool ans;
int n,m;
bool check() {    //判断在这种(n行配对)的情况下,是否可以通过列的转换实现对称 
	memset(used,0,sizeof(used));
	for(int i=0; i<m; i++) {       //枚举任意不相等的两列,在这种(n行配对)的情况下,这两列是否相等 
		if(used[i])                //相等则说明这两列可以通过列变换形成对称 
			continue;
		for(int j=0; j<m; j++) {
			bool vis=true;
			if(i==j||used[j])
				continue;
			for(int k=0; k<n; k++) {
				if(str[k][i]==str[swaped[k]][j])
					continue;
				vis=false;
				break;
			}
			if(vis){
				used[i]=used[j]=true;
				break;
			}
		}
	}
	int cnt=0;
	int pos=-1;
	for(int i=0; i<m; i++)   //计算有多少列没配对 
		if(!used[i]) {
			cnt++;
			pos=i;
		}
	if(cnt>=2||(cnt&&(m%2==0))) //没配对数大于等于2或者(列数为奇数且没配对数等于1) 
		return false;           //的情况都说明这种(n行配对)的情况无法通过列转移实现对称 
	if(cnt==1) {                //对于列数m为奇数的情况,需要特判这一列是否可以形成对称 
		for(int i=0; i<n; i++) {
			if(str[i][pos]!=str[swaped[i]][pos])
				return false;
		}
	}
	return true;
}
void dfs(int u,int falg) {   //dfs枚举出所有的(n行配对)的情况 
	if(u==(n+1)>>1) {      //u==(n+1)/2,说明所有的行已经配对成功,这是一种(n行配对)的情况 
		if(check())       //判断列是否通过转换形成对称 
			ans=true;
		return ;
	}
	if(ans==true)
	return;
	for(int i=u; i<n; i++) {
		if(swaped[i]==-1) {
			for(int j=i+1; j<n; j++) {
				if(swaped[j]==-1) {
					swaped[i]=j;
					swaped[j]=i;
					dfs(u+1,falg);
					swaped[i]=-1;
					swaped[j]=-1;
				}
			}
			if(falg) {     //对于行数n为奇数的情况,有一行是关于没有对称行的 
				swaped[i]=i;
				dfs(u+1,false);
				swaped[i]=-1;
			}
		}
	}
}
void init() {
	memset(swaped,-1,sizeof(swaped));
	ans=false;
}
int main() {
	while(~scanf("%d %d",&n,&m)) {
		init();
		for(int i=0; i<n; i++)
			scanf("%s",str[i]);
		if(n&1)
			dfs(0,true);
		else
			dfs(0,false);
		printf("%s\n",ans?"YES":"NO");
	}
	return 0;
}

F - Permutation Tree

题目链接

看这位大佬的分析得到的灵感点击打开链接

题意:给你一个序列(p1,p2,…,pn) 对于每一个结点(1,2,…,n),两种操作

1.如果pi=1,无需任何操作

2.如果pi1,找出最大的j,满足pj<pi,则连接结点i和结点j.

通过序列(p1,p2,…,pn) 可以唯一的构造一棵树。

题目给你n表示有n个结点,然后n-1条边构造一棵树。问是否可以输出一个(p1,p2,…,pn) 序列,通过这个序列构造的树和题目给出的树同构,可以的话,输出满足这个条件的最小字典序的序列。无解的话输出-1。

思路:一开始所有结点先染成黑色,对(p1,p2,…,pn) 序列进行升序排序,考虑的时候仍为原来的i值,定义一个变量tmp=-1,对于排序后的(p1,p2,…,pn) 序列,从左往右扫一遍,对于pi的i>tmp的,对i这个结点染成红色,然后tmp=i;

染色结束后,红色的结点连成的边形成的路径即为这棵树的直径,黑点都一个一个的连接在这些直径上,黑点不会和黑点相连。这样形成的图暂称为“毛虫图”吧。

如果有黑点和黑点直接相连,则肯定写不出(p1,p2,…,pn) 序列的(可以想一下这个样例:

7
1 2
2 3
3 4
4 5
3 6

6 7

(答案:-1)

直径染成红色后,还有两个黑点相连,则没办法写出 (p1,p2,…,pn) 序列。

做法是先求出这个树的直径,判断是否能构成“毛虫图”,可以的话,以直径为主,判断以直径的哪一个端点开始为起点,然后重新分配结点的下标,贪心保证字典序最小,具体看代码。

#include<bits/stdc++.h>
using namespace std;
const int maxn=400000+10;
int head[maxn];  //前向星的写法,头数组的记录 
bool vis[maxn];  //标记数组 
int num[maxn];   //记录有多少个子节点 
int node[maxn];  //记录结点到子树的结点的最大深度 
int que[maxn];   //模拟数组 
int nod[maxn];   //记录图的直径上的结点(不包括起点和终点)的连接其他零散点(即不在直径上的点)的个数 
struct Edge {
	int u,v;
	int next;
} edge[maxn];
int n,m;        //n个点,m条边 
int tol;        //前向星的写法,记录边的总数 
int cnt;        //直径上的点的数量记录 
int ans;        //答案输出的,因为要最小序列输出,ans不断变化,要记录为全局变量 
int S,T;        //树的直径的起点和终点 
bool flag;      //flag记录是否可以构成"毛虫图" 
void init() {   //初始化 
	memset(head,-1,sizeof(head));
	memset(node,0,sizeof(node));
	memset(num,0,sizeof(num));
	tol=0;
	cnt=0;
	ans=1;
	flag=true;
}
void add(int u,int v) {  //前向星的写法,添加边 
	edge[tol].u=u;
	edge[tol].v=v;
	edge[tol].next=head[u];
	head[u]=tol++;
}
int bfs(int s) {         //bfs求一个点到另一个最远点的距离 
	int l=0,r=0;
	memset(vis,0,sizeof(vis));
	memset(que,0,sizeof(que));
	que[r++]=s;
	vis[s]=1;
	while(l<=r) {
		s=que[l++];
		for(int i=head[s]; i!=-1; i=edge[i].next) {
			int v=edge[i].v;
			if(vis[v]==0) {
				vis[v]=1;
				que[r++]=v;
			}
		}
	}
	return que[r-1];
}
void dfs(int u) {     //dfs求出结点到子树的结点的最大深度(node[]),判断是否可以构成"毛虫图” 
	if(!flag)         //以及,树的直径上的结点连接其他零散点的个数 
		return;
	int top=0;
	vis[u]=1;
	for(int i=head[u]; i!=-1; i=edge[i].next) {
		int v=edge[i].v;
		if(vis[v]==0) {
			dfs(v);
			node[u]=max(node[u],node[v]);
			if(node[v]>1) top++;
			if(v!=T&&node[v]==1)
				num[u]++;
		}
	}
	node[u]++;
	if(top>=2) {   //一个结点,连接有两个及以上的node[]>1的结点 ,肯定无法构成"毛虫图" 
		flag=false;
		return;
	}
	if(u!=S&&u!=T&&node[u]>=2)
		nod[cnt++]=num[u];
}
void printf_(int u) {  //用递归的方法输出ans,对于num[u]>0的数,要先输出它的连接点 
	int t=ans;
	vis[u]=1;
	if(num[u]>0) {
		for(int i=1; i<=num[u]; i++) {
			printf("%d ",++ans);
		}
	}
	printf("%d%c",t,u==T?'\n':' ');
	ans++;
	for(int i=head[u]; i!=-1; i=edge[i].next) {
		int v=edge[i].v;
		if(v==T||v==S||node[v]>1) {
			if(!vis[v])
				printf_(v);
		}
	}
}
int read() {   //数据输入有点大,想试试能快几ms的 
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9') {
		if(ch=='-')
			f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch) {
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
}
int main() {
	while(~scanf("%d",&n)) {
		init();
		m=n-1;
		int u,v;
		for(int i=0; i<m; i++) {
			//scanf("%d %d",&u,&v);
			u=read();
			v=read();
			add(u,v);
			add(v,u);
		}
		T=bfs(1);
		S=bfs(T);
		memset(vis,0,sizeof(vis));
		dfs(S);
		/*cout<<S<<" "<<T<<endl;
		cout<<cnt<<endl;
		for(int i=1; i<=n; i++)
			printf("%d%c",node[i],i==n?'\n':' ');
		for(int i=1; i<=n; i++)
			printf("%d%c",num[i],i==n?'\n':' ');
		for(int i=0; i<cnt; i++)
			printf("%d%c",nod[i],i==cnt-1?'\n':' ');
		*/
		reverse(nod,nod+cnt);   //dfs得到的nod[]其实是从T到S的nod[]数据,reverse反转元素的顺序,得到S到T的nod[]数据 
		for(int i=0; i<cnt; i++) {
			if(nod[i]<nod[cnt-i-1])   //nod[i]越大的应该也越往后才输出,这样保证输出的字典序最小 
				break;
			else if(nod[i]>nod[cnt-i-1]) {
				swap(S,T);
				break;
			}
		}
		memset(vis,0,sizeof(vis));
		if(!flag)
			printf("-1\n");
		else
			printf_(S);
	}
}
第一次打AtCoder,不小心报了Beginner的,我真的菜爆了╮( ̄▽ ̄"")╭

C - Many Medians

题目链接

题意:给n个数(n为偶数),然后问除第i个数以后的(n-1)个数的第(n-1)/2大是多少,输出所有的i.

思路:对原数组copy一份numa[],排序,找出中间的第n/2大和第(n/2)+1大;对于原数组任意一个numb[i],如果numb[i]<=numa[n/2],则剔除numb[i]后,数组numa[]的中间那个值,即第(n-1)/2大的值会向右移一位;如果numb[i]>numa[n/2],则剔除numb[i]后,数组numa[]的中间那个值,即为第(n-1)/2大的值。

#include<bits/stdc++.h>
using namespace std;
const int maxn=200000+10;
int numa[maxn];
int numb[maxn];
int main() {
	int n;
	while(~scanf("%d",&n)) {
		for(int i=1; i<=n; i++) {
			scanf("%d",&numa[i]);
			numb[i]=numa[i];
		}
		sort(numa+1,numa+n+1);
		int m=n/2;
		for(int i=1; i<=n; i++) {
			if(numb[i]<=numa[m])
				printf("%d\n",numa[m+1]);
			else
				printf("%d\n",numa[m]);
		}
	}
	return 0;
}

D - Binomial Coefficients

题目链接

题意:给出n个数,求这n个数中任取两个数a和b,求能使Comb(a,b)最大的a和b;

思路:简单结论题,通过推理可以得知,对于n个数中的最大值num[m],必定存在一个数num[k],使得num[k]最接近num[m]/2这个值,则Comb(num[m],num[k])>=Comb(num[m-1],num[k])>=Comb(num[m-1],num[k-1]),故Comb(num[m],num[k])为最大值,求出num[m]和num[k]即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn=200000+10;
int num[maxn];
int cmd(int a,int b) {
	return a>b;
}
int main() {
	int n;
	while(~scanf("%d",&n)) {
		for(int i=0; i<n; i++)
			scanf("%d",&num[i]);
		sort(num,num+n);
		int m=num[n-1];
		int posa=lower_bound(num,num+n,m/2)-num;
		int a=num[posa];   //从右边最逼近m/2的值 
		sort(num,num+n,cmd);
		int posb=lower_bound(num,num+n,m/2,greater<int>())-num;
		int b=num[posb];   //从左边最逼近m/2的值 
		int ans;
		if((m-a)>b)        //算出哪个逼近的值最优 
			ans=a;
		else
			ans=b;
		printf("%d %d\n",m,ans);
	}
	return 0;
}

E - Symmetric Grid

题目链接

题意:给一个n行m列的矩阵,求是否可以通过行和列的转换变成一个对称矩阵。

思路:swaped[]数组记录配对情况,比如记录swaped[i]=j,swaped[j]=i,意思是第i行和第j行是关于中间n/2行对称的(这是假设的),然后通过dfs枚举出所有的关于行的对称情况(当n为奇数时,有一行关于自己对称,可以理解为这恰好为对称矩阵的第(n+1)/2行)因为对称关系是两两配对的,所以第step==(n+1)/2时,刚好实现所有n行的配对情况,我暂时称为这是一种(n行配对)情况,需要判断它的所有列是否能通过转移达到对称。
这里通过一个check()函数实现,对于一种(n行配对)情况--这种情况是后面列配对的前提,通过对列进行配对,配对的条件是所有的第i列的k行的内容和所有的第j列的swaped[k]行的内容对应相等,如果可以,说明可以通过转移列,使它们在对称的位置。然后计算所有没配对的列,结果为cnt。如果cnt>=2或者列数m为偶数,但cnt==1,即有一列没配对,这些都说明在这种(n行配对)的情况下,无法通过列的转移实现矩阵的对称。如果刚好cnt==1&&列数m为奇数,这说明这一列刚好是这矩阵的中间一列,需要判断这一列是否在(n行配对)的情况下对称。如果也对称了,说明这个矩阵在(n行配对)的情况下相应
转移行,再相应的转换列,便可以实现对称矩阵。

举个栗子,

4 5
rrdca
adrcr
brdgt

tdrgb

(答案:YES)

在swaped[0]=1,swaped[1]=0,swaped[2]=3,swaped[3]=2的n行配对情况下,即第0行和第1行

是对称的,第2行和第3行是对称的。第1列可以和第2列相应配对(转换),说明第2列和第3列可以通过

转移到对称的位置上。同理,第1列和第5列相应配对,剩下第3列没配对,进行特判。

#include<bits/stdc++.h>
using namespace std;
const int maxn=20+10;
const int INF=int(1e9)+10;
#define LL long long
char str[maxn][maxn];
int swaped[maxn];
bool used[maxn];
bool ans;
int n,m;
bool check() {    //判断在这种(n行配对)的情况下,是否可以通过列的转换实现对称 
	memset(used,0,sizeof(used));
	for(int i=0; i<m; i++) {       //枚举任意不相等的两列,在这种(n行配对)的情况下,这两列是否相等 
		if(used[i])                //相等则说明这两列可以通过列变换形成对称 
			continue;
		for(int j=0; j<m; j++) {
			bool vis=true;
			if(i==j||used[j])
				continue;
			for(int k=0; k<n; k++) {
				if(str[k][i]==str[swaped[k]][j])
					continue;
				vis=false;
				break;
			}
			if(vis){
				used[i]=used[j]=true;
				break;
			}
		}
	}
	int cnt=0;
	int pos=-1;
	for(int i=0; i<m; i++)   //计算有多少列没配对 
		if(!used[i]) {
			cnt++;
			pos=i;
		}
	if(cnt>=2||(cnt&&(m%2==0))) //没配对数大于等于2或者(列数为奇数且没配对数等于1) 
		return false;           //的情况都说明这种(n行配对)的情况无法通过列转移实现对称 
	if(cnt==1) {                //对于列数m为奇数的情况,需要特判这一列是否可以形成对称 
		for(int i=0; i<n; i++) {
			if(str[i][pos]!=str[swaped[i]][pos])
				return false;
		}
	}
	return true;
}
void dfs(int u,int falg) {   //dfs枚举出所有的(n行配对)的情况 
	if(u==(n+1)>>1) {      //u==(n+1)/2,说明所有的行已经配对成功,这是一种(n行配对)的情况 
		if(check())       //判断列是否通过转换形成对称 
			ans=true;
		return ;
	}
	if(ans==true)
	return;
	for(int i=u; i<n; i++) {
		if(swaped[i]==-1) {
			for(int j=i+1; j<n; j++) {
				if(swaped[j]==-1) {
					swaped[i]=j;
					swaped[j]=i;
					dfs(u+1,falg);
					swaped[i]=-1;
					swaped[j]=-1;
				}
			}
			if(falg) {     //对于行数n为奇数的情况,有一行是关于没有对称行的 
				swaped[i]=i;
				dfs(u+1,false);
				swaped[i]=-1;
			}
		}
	}
}
void init() {
	memset(swaped,-1,sizeof(swaped));
	ans=false;
}
int main() {
	while(~scanf("%d %d",&n,&m)) {
		init();
		for(int i=0; i<n; i++)
			scanf("%s",str[i]);
		if(n&1)
			dfs(0,true);
		else
			dfs(0,false);
		printf("%s\n",ans?"YES":"NO");
	}
	return 0;
}

F - Permutation Tree

题目链接

看这位大佬的分析得到的灵感点击打开链接

题意:给你一个序列(p1,p2,…,pn) 对于每一个结点(1,2,…,n),两种操作

1.如果pi=1,无需任何操作

2.如果pi1,找出最大的j,满足pj<pi,则连接结点i和结点j.

通过序列(p1,p2,…,pn) 可以唯一的构造一棵树。

题目给你n表示有n个结点,然后n-1条边构造一棵树。问是否可以输出一个(p1,p2,…,pn) 序列,通过这个序列构造的树和题目给出的树同构,可以的话,输出满足这个条件的最小字典序的序列。无解的话输出-1。

思路:一开始所有结点先染成黑色,对(p1,p2,…,pn) 序列进行升序排序,考虑的时候仍为原来的i值,定义一个变量tmp=-1,对于排序后的(p1,p2,…,pn) 序列,从左往右扫一遍,对于pi的i>tmp的,对i这个结点染成红色,然后tmp=i;

染色结束后,红色的结点连成的边形成的路径即为这棵树的直径,黑点都一个一个的连接在这些直径上,黑点不会和黑点相连。这样形成的图暂称为“毛虫图”吧。

如果有黑点和黑点直接相连,则肯定写不出(p1,p2,…,pn) 序列的(可以想一下这个样例:

7
1 2
2 3
3 4
4 5
3 6

6 7

(答案:-1)

直径染成红色后,还有两个黑点相连,则没办法写出 (p1,p2,…,pn) 序列。

做法是先求出这个树的直径,判断是否能构成“毛虫图”,可以的话,以直径为主,判断以直径的哪一个端点开始为起点,然后重新分配结点的下标,贪心保证字典序最小,具体看代码。

#include<bits/stdc++.h>
using namespace std;
const int maxn=400000+10;
int head[maxn];  //前向星的写法,头数组的记录 
bool vis[maxn];  //标记数组 
int num[maxn];   //记录有多少个子节点 
int node[maxn];  //记录结点到子树的结点的最大深度 
int que[maxn];   //模拟数组 
int nod[maxn];   //记录图的直径上的结点(不包括起点和终点)的连接其他零散点(即不在直径上的点)的个数 
struct Edge {
	int u,v;
	int next;
} edge[maxn];
int n,m;        //n个点,m条边 
int tol;        //前向星的写法,记录边的总数 
int cnt;        //直径上的点的数量记录 
int ans;        //答案输出的,因为要最小序列输出,ans不断变化,要记录为全局变量 
int S,T;        //树的直径的起点和终点 
bool flag;      //flag记录是否可以构成"毛虫图" 
void init() {   //初始化 
	memset(head,-1,sizeof(head));
	memset(node,0,sizeof(node));
	memset(num,0,sizeof(num));
	tol=0;
	cnt=0;
	ans=1;
	flag=true;
}
void add(int u,int v) {  //前向星的写法,添加边 
	edge[tol].u=u;
	edge[tol].v=v;
	edge[tol].next=head[u];
	head[u]=tol++;
}
int bfs(int s) {         //bfs求一个点到另一个最远点的距离 
	int l=0,r=0;
	memset(vis,0,sizeof(vis));
	memset(que,0,sizeof(que));
	que[r++]=s;
	vis[s]=1;
	while(l<=r) {
		s=que[l++];
		for(int i=head[s]; i!=-1; i=edge[i].next) {
			int v=edge[i].v;
			if(vis[v]==0) {
				vis[v]=1;
				que[r++]=v;
			}
		}
	}
	return que[r-1];
}
void dfs(int u) {     //dfs求出结点到子树的结点的最大深度(node[]),判断是否可以构成"毛虫图” 
	if(!flag)         //以及,树的直径上的结点连接其他零散点的个数 
		return;
	int top=0;
	vis[u]=1;
	for(int i=head[u]; i!=-1; i=edge[i].next) {
		int v=edge[i].v;
		if(vis[v]==0) {
			dfs(v);
			node[u]=max(node[u],node[v]);
			if(node[v]>1) top++;
			if(v!=T&&node[v]==1)
				num[u]++;
		}
	}
	node[u]++;
	if(top>=2) {   //一个结点,连接有两个及以上的node[]>1的结点 ,肯定无法构成"毛虫图" 
		flag=false;
		return;
	}
	if(u!=S&&u!=T&&node[u]>=2)
		nod[cnt++]=num[u];
}
void printf_(int u) {  //用递归的方法输出ans,对于num[u]>0的数,要先输出它的连接点 
	int t=ans;
	vis[u]=1;
	if(num[u]>0) {
		for(int i=1; i<=num[u]; i++) {
			printf("%d ",++ans);
		}
	}
	printf("%d%c",t,u==T?'\n':' ');
	ans++;
	for(int i=head[u]; i!=-1; i=edge[i].next) {
		int v=edge[i].v;
		if(v==T||v==S||node[v]>1) {
			if(!vis[v])
				printf_(v);
		}
	}
}
int read() {   //数据输入有点大,想试试能快几ms的 
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9') {
		if(ch=='-')
			f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch) {
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
}
int main() {
	while(~scanf("%d",&n)) {
		init();
		m=n-1;
		int u,v;
		for(int i=0; i<m; i++) {
			//scanf("%d %d",&u,&v);
			u=read();
			v=read();
			add(u,v);
			add(v,u);
		}
		T=bfs(1);
		S=bfs(T);
		memset(vis,0,sizeof(vis));
		dfs(S);
		/*cout<<S<<" "<<T<<endl;
		cout<<cnt<<endl;
		for(int i=1; i<=n; i++)
			printf("%d%c",node[i],i==n?'\n':' ');
		for(int i=1; i<=n; i++)
			printf("%d%c",num[i],i==n?'\n':' ');
		for(int i=0; i<cnt; i++)
			printf("%d%c",nod[i],i==cnt-1?'\n':' ');
		*/
		reverse(nod,nod+cnt);   //dfs得到的nod[]其实是从T到S的nod[]数据,reverse反转元素的顺序,得到S到T的nod[]数据 
		for(int i=0; i<cnt; i++) {
			if(nod[i]<nod[cnt-i-1])   //nod[i]越大的应该也越往后才输出,这样保证输出的字典序最小 
				break;
			else if(nod[i]>nod[cnt-i-1]) {
				swap(S,T);
				break;
			}
		}
		memset(vis,0,sizeof(vis));
		if(!flag)
			printf("-1\n");
		else
			printf_(S);
	}
}
第一次打AtCoder,不小心报了Beginner的,我真的菜爆了╮( ̄▽ ̄"")╭
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值