2017.12.02【NOIP提高组】模拟赛A组

2017.12.02【NOIP提高组】模拟赛A组

T1 3555【GDKOI2014模拟】树的直径
T2 3542【清华集训2014】冒泡排序
T3 3486【NOIP2013模拟联考10】道路改建(rebuild)

T1

树直径的一个性质,两棵树合并,形成新的树的直径的两个端点为原树中的四个端点之二。
可以用反证法证明。用此性质本题就变成了lca裸题了

Code

#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#include<bitset>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,x) for(int i=head[x];i;i=next[i])
#define mem(a,x) memset(a,x,sizeof(a))
typedef long long LL;
typedef double DB;
using namespace std;
inline int max(int x,int y) {return (x>y)?x:y;}
inline int min(int x,int y) {return (x>y)?y:x;}
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9') f=(ch=='-')?-1:f,ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+(ch-'0'),ch=getchar();return f*x;
}
const int N=200000+50;
int to[N<<1],next[N<<1],tot,head[N],dep[N],go[N][17],s,t,mx,m,x,son;
void add(int x,int y) {to[++tot]=y,next[tot]=head[x],head[x]=tot;}
void dfs(int x) {rep(i,x) dep[to[i]]=dep[x]+1,go[to[i]][0]=x,dfs(to[i]);}
void init_lca() {
	dep[1]=1,dfs(1);
	fo(i,1,16) fo(x,1,2*m+4) go[x][i]=go[go[x][i-1]][i-1];
}
int lca(int a,int b) {
	if(dep[a]<dep[b]) swap(a,b);
	int f=dep[a]-dep[b];
	fo(i,0,16) if(f&(1<<i)) a=go[a][i];
	if(a==b) return b;
	fd(i,16,0) if(go[a][i]!=go[b][i]) a=go[a][i],b=go[b][i];
	return go[a][0];
}
int main() {
	m=read();
	add(1,2),add(1,3),add(1,4);
	fo(i,1,m) add((x=read()),(son=(i-1)*2+5)),add(x,son+1);
	init_lca();
	s=2,t=4,mx=2;
	fo(i,1,m) {
		int son1=(i-1)*2+5,fa1,fa2,dis1,dis2,_s,_t,_dis;
		fa1=lca(son1,s);
		dis1=dep[son1]+dep[s]-2*dep[fa1];
		fa2=lca(son1,t);
		dis2=dep[son1]+dep[t]-2*dep[fa2];
		if(dis1>dis2) _s=son1,_t=s,_dis=dis1;
		else _s=son1,_t=t,_dis=dis2;
		if(_dis>mx) s=_s,t=_t,mx=_dis;
		printf("%d
",mx);
	}
	return 0;
}

T2

一看是清华集训题就慌了,但仔细一想还是十分简单的。

若一个数之前有t个比它要大,那么前t轮中每轮它会往前移1个位置,之后便不再会往前移了。
考虑到交换次数=前移次数,可据此求出最大的轮数x使得做了x轮冒泡后交换次数刚好不超过k。
那些t>=x的数的位置前移了x,所有t<x的数之间的顺序已经排好,按顺序填入剩下的位置即可。
求出了做了x轮之后的序列以后再模拟冒泡的过程做剩下的几次就好了。

Code

#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,x) for(int i=head[x];i;i=next[i])
#define mem(a,x) memset(a,x,sizeof(a))
typedef long long LL;
typedef double DB;
using namespace std;
inline int max(int x,int y) {return (x>y)?x:y;}
inline int min(int x,int y) {return (x>y)?y:x;}
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9') f=(ch=='-')?-1:f,ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+(ch-'0'),ch=getchar();return f*x;
}
const int N=1000001;
int a[N],upper[N],ans[N],sa[N],tr[N],n;
inline int lowbit(int x) {return x&(-x);}
inline void add(int x,int xx) {for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=xx;}
inline int sum(int x) {
     int ans=0;
     for(int i=x;i>=1;i-=lowbit(i)) ans+=tr[i];
     return ans;
}
int main()
{
     LL k,sx=0;
     n=read(),scanf("%lld",&k);
	 fo(i,1,n) a[i]=read();
	 fo(i,1,n) upper[i]=sum(n)-sum(a[i]),add(a[i],1);
     int l=1,r=n;
     while(l<=r) {
          int mid=(l+r)>>1;
          sx=0;
          fo(i,1,n) sx+=(LL)min(mid,upper[i]);
          if(sx<=k) l=mid+1;
          else r=mid-1;
     }
     sx=0;
     fo(i,1,n) sx+=(LL)min(r+1,upper[i]);
     LL m=k-sx;
     if(m>0) {printf("Impossible!
");return 0;}
     sx=0;
     fo(i,1,n) sx+=(LL)min(r,upper[i]);
     m=k-sx;
     int p=0;
     fo(i,1,n)
          if(upper[i]>r) ans[i-r]=a[i];
          else p++,sa[p]=a[i];
     sort(sa+1,sa+1+p);
     p=0;
     fo(i,1,n) if(ans[i]==0) p++,ans[i]=sa[p];
     for(int i=1;i<n&&m!=0;i++) {
          if(ans[i]>ans[i+1]) {
               int t=ans[i];
               ans[i]=ans[i+1];
               ans[i+1]=t;
               m--;
          }
     }
     fo(i,1,n-1) printf("%d ",ans[i]);
     printf("%d
",ans[n]);
     return 0;
}

T3(意外在OJ上莫名打不开题目2017.12.9)

只能凭印象写一下题解了(记性不好,算了 等OJ好了再补齐吧)

原文地址:https://www.cnblogs.com/patricksu/p/8011559.html