AtCoder Regular Contest 095
AtCoder Regular Contest 095
C - Many Medians
题意:
给出n个数,求出去掉第i个数之后所有数的中位数,保证n是偶数。
\(n\le 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_i\ge a_j\),使\(C_{a_i}^{a_j}\)最大。
\(n\le 100000\)
分析:
观察题目范围,我们可以用\(nlogn\)的复杂度解决问题。
考虑组合数的几个式子:
1.\(C_n^k\ge C_{n-1}^k\)
2.\(C_n^k\ge 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?'\n':' ');
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);
}