AtCoder Regular Contest 095

AtCoder Regular Contest 095


C - Many Medians

题意:

给出n个数,求出去掉第i个数之后所有数的中位数,保证n是偶数。
(nle 200000)

分析:

发现题目范围支持(nlogn)做法。

我们可以对这些数字建立一棵平衡树,先全加进去,再一边删除一边find一边再加,非常容易想到的解法,但是真要这么写估计排名高不到哪里去。

考虑删除一个数之后中位数的情况,明显只有两种,一种是你删除的数小于等于中位数,还一种是大于中位数,那么可以先排序找出中位数,然后遍历,判断每个数和中位数的大小关系,可以得出正确结果。

#include <bits/stdc++.h>
using namespace std;
const int MAXN=2e5+10;
int a[MAXN],b[MAXN];
int main()
{
	int n;
	while(cin>>n){
		for(int i=1;i<=n;i++){
			cin>>a[i];
			b[i]=a[i];
		}
		sort(b+1,b+n+1);
		int mid=n/2;
		for(int i=1;i<=n;i++){
			if(a[i]<=b[mid]) cout<<b[mid+1]<<endl;
			else cout<<b[mid]<<endl;
		}
	}
}

D - Binomial Coefficients

题意:

给出n个数,从中选取两个数(a_{i},a_{j}),且(a_ige a_j),使(C_{a_i}^{a_j})最大。
(nle 100000)

分析:

观察题目范围,我们可以用(nlogn)的复杂度解决问题。

考虑组合数的几个式子:

1.(C_n^kge C_{n-1}^k)
2.(C_n^kge C_n^{k-1})

所以直接找到最大的数(n),然后找到一个最接近于(n/2)的数(k)就一定是最优的。
发现可以使用平衡树,首先直接全部插入splay中,然后取得最大的,把(n/2)插入,求前驱后继,然后比较一下就行了,但是这么写明显不会有好的排名。

可以在输入时先找到最大的数,然后再遍历一遍数组,容易找出结果。

#include <bits/stdc++.h>
using namespace std;
int a[1000007],n,id;
int main()
{
	cin>>n;
	int maxx=-1;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		if(a[i]>maxx){
			maxx=a[i];
			id=i;
		}
	}
	int mid=maxx/2+(maxx%2==1?1:0),ans,cnt=1999999999;
	for(int i=1;i<=n;i++){
		if(i==id) continue;
		if(abs(mid-a[i])<=cnt){
			cnt=abs(mid-a[i]);
			ans=a[i];
		}
	}
	cout<<maxx<<" "<<ans;
}

E - Symmetric Grid

题意:

给出一个由字符组成的矩阵,可以任意交换某两行或者某两列,问是否能将这个矩形交换成对称的。

分析:

思路非常清晰,代码比较复杂。

先判断所有行的最终排列,然后在这个基础上check这时的所有列能否满足两两匹配,如果不能匹配的少于两个的话就可能是一个可行解。

提出另一种解法:可以直接看出每行的各字符数不会发生任何变化,每列的各字符数不会发生任何变化。并且如果找到一组可行解,可以随便配对行列的排列顺序,保证得出的还是一个可行解。

那么是否可以直接把每行的字符排序之后的字符串扔到一个map里面,然后判断是否能配对,同样我们对列也这么做,貌似可以做到极其优秀的复杂度,不过笔者并没有尝试。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN=30;
char st[MAXN][MAXN];
int sp[MAXN],ans,used[MAXN];
int n,m;
bool check()
{
	memset(used,0,sizeof(used));
	for(int i=0;i<m;i++){
		if(used[i]) continue;
		for(int j=0;j<m;j++){
			bool vis=1;
			if(i==j||used[j]) continue;
			for(int k=0;k<n;k++){
				if(st[k][i]==st[sp[k]][j]) continue;
				vis=0;
				break;
			}
			if(vis){
				used[i]=used[j]=1;
				break;
			}
		}
	}
	int cnt=0,pos=-1;
	for(int i=0;i<m;i++)
		if(!used[i]) cnt++,pos=i;
	if(cnt>=2||(cnt&&(m%2==0))) return 0;
	if(cnt==1)
		for(int i=0;i<n;i++)
			if(st[i][pos]!=st[sp[i]][pos]) return 0;
	return 1;
}
void dfs(int u,int flag)
{
	if(u==(n+1)>>1){
		if(check()) ans=1;
		return;
	}
	if(ans==1) return;
	for(int i=u;i<n;i++){
		if(sp[i]==-1){
			for(int j=i+1;j<n;j++){
				if(sp[j]==-1){
					sp[i]=j;sp[j]=i;
					dfs(u+1,flag);
					sp[i]=sp[j]=-1;
				}
			}
			if(flag){
				sp[i]=i;
				dfs(u+1,0);
				sp[i]=-1;
			}
		}
	}
}
int main()
{
	while(cin>>n>>m){
		memset(sp,-1,sizeof(sp));
		ans=0;
		for(int i=0;i<n;i++) scanf("%s",st[i]);
		if(n&1) dfs(0,1);
		else dfs(0,0);
		if(ans) cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
	}
}

F - Permutation Tree

题意:

给你一个序列((p_1,p_2,…,p_n)对于每一个结点)(1,2,…,n)$,两种操作

1.如果(p_i=1),无需任何操作
2.如果(p_i≠1),找出最大的j,满足pj<pi,则连接结点i和结点j.

通过序列((p_1,p_2,…,p_n))可以唯一的构造一棵树。

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

分析:

先不分析了。

#include <bits/stdc++.h>
using namespace std;
const int MAXN=4e5+7;
int head[MAXN],vis[MAXN],size[MAXN],dep[MAXN],nod[MAXN];
struct po
{
	int nxt,to,from;
}edge[MAXN];
int n,m,num,cnt,ans=1,s,t,flag=1;
inline void add_edge(int from,int to)
{
	edge[++num].nxt=head[from];
	edge[num].from=from;
	edge[num].to=to;
	head[from]=num;
}
inline int bfs(int S)
{
	memset(vis,0,sizeof(vis));
	queue<int> q;
	q.push(S);
	vis[S]=1;
	int u;
	while(!q.empty()){
		u=q.front(); q.pop();
		for(int i=head[u];i;i=edge[i].nxt){
			int v=edge[i].to;
			if(!vis[v]){
				vis[v]=1;
				q.push(v);
			}
		}
	}
	return u;
}
void dfs(int u)
{
	if(!flag) return;
	int tp=0;
	vis[u]=1;
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].to;
		if(!vis[v]){
			dfs(v);
			dep[u]=max(dep[u],dep[v]);
			if(dep[v]>1) tp++;
			if(v!=t&&dep[v]==1) size[u]++;
		}
	}
	dep[u]++;
	if(tp>=2){
		flag=0;
		return;
	}
	if(u!=s&&u!=t&&dep[u]>=2)
		nod[cnt++]=size[u];
}
void out(int u)
{
	int tt=ans;
	vis[u]=1;
	if(size[u]>0) for(int i=1;i<=size[u];i++) cout<<++ans<<" ";
	printf("%d%c", tt,u==t?'
':' ');
	ans++;
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].to;
		if(v==t||v==s||dep[v]>1){
			if(!vis[v]) out(v);
		}
	}
}
int main()
{
	cin>>n;
	m=n-1;
	for(int i=1;i<=m;i++){
		int u,v;
		cin>>u>>v;
		add_edge(u,v);
		add_edge(v,u);
	}
	t=bfs(1);s=bfs(t);
	memset(vis,0,sizeof(vis));
	dfs(s);
	
	reverse(nod,nod+cnt);
	for(int i=0;i<cnt;i++){
		if(nod[i]<nod[cnt-i-1]) break;
		else if(nod[i]>nod[cnt-i-1]){
			swap(s,t);
			break;
		}
	}
	memset(vis,0,sizeof(vis));
	if(!flag) cout<<"-1";
	else out(s);
}
原文地址:https://www.cnblogs.com/victorique/p/9582788.html