noip 2018 模拟赛12

T 1 T_1 T1——gold(3079)

Description:

有一个 n ⋅ m n \cdot m nm的矩形,对于每一行,可以选择前 k , k ∈ [ 1 , n ] k,k\in[1,n] k,k[1,n]个数。
求全部选完后的平均值的最大值。
n ⋅ m ≤ 1 0 5 , A i , j ≤ 1 0 9 n \cdot m\le 10^5,A_{i,j}\le 10^9 nm105,Ai,j109

Solution:

  • 一看到求平均数、中位数之类的,有极大可能是要二分答案的。
  • 对于check,我们统计出每一行的前 k k k个数与 m i d mid mid的差值和的最大值的和,即比较当前mid是否最优。
  • 这样复杂度为 Θ ( n m log ⁡ ( 1 0 9 ) ) \Theta(nm\log(10^9)) Θ(nmlog(109))

Code:

#include<bits/stdc++.h>
using namespace std;
#define SREP(i,f,t) for(int i=(f),i##_end_=(t);i<i##_end_;++i)
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
#define db double

const db inf=1.0/0.0;
const int N=1e5+2;

int n,m;
vector<int>A[N];

bool check(db x){
	db res=0;
	SREP(i,0,n){
		db mx=-inf,sum=0;
		SREP(j,0,m){
			sum+=A[i][j]-x;
			chkmax(mx,sum);
		}
		res+=mx;
	}
	return res>=0;
}

int main(){
//	freopen("gold.in","r",stdin);
//	freopen("gold.out","w",stdout);
	scanf("%d%d",&n,&m);
	SREP(i,0,n){
		A[i].resize(m);
		SREP(j,0,m) scanf("%d",&A[i][j]);
	}
	db l=0,r=1e9;
	while(r-l>1e-6){
		db mid=1.0*(l+r)/2;
		if(check(mid)) l=mid;
		else r=mid;
	}
	printf("%.4lf\n",l);
	return 0;
}

T 2 T_2 T2——road(3080)

Description:

有两排城市,每一排的城市编号都为1~n,对于每对编号相同的城市建一条边。
求最大的城市集合,满足集合内城市之间的边都相交。
n ≤ 1 0 5 n\le 10^5 n105

Solution:

  • 通过模拟小数据,将第二排的城市编号翻转,发现,符合的城市集合即为两排城市的公共子序列。
  • 那么问题就是求最长公共子序列了。
  • 首先有一个 Θ ( n 2 ) \Theta(n^2) Θ(n2)求最长公共子序列的 d p dp dp,但复杂度显然不够。
  • 所以就需要数据结构优化一下,达到 Θ ( n log ⁡ n ) \Theta(n\log n) Θ(nlogn),这里线段树或者树状数组都行。

Code:

#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
template<class T>inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
template<class T>inline void Rd(T &x){
    x=0;char c;
    while((c=getchar())<48);
    do x=(x<<1)+(x<<3)+(c^48);
    while((c=getchar())>47);
}

const int N=100005;

int n;
int A[N],B[N],T[N];

int bit[N];
void add(int x,int v){
	for(;x;x-=(x&-x)) chkmax(bit[x],v);
}
int query(int x){
	int res=0;
	for(;x<N;x+=(x&-x)) chkmax(res,bit[x]);
	return res;
}

int main(){
//	freopen("road.in","r",stdin);
//	freopen("road.out","w",stdout);
	scanf("%d",&n);
	REP(i,1,n) Rd(A[i]);
	REP(i,1,n) Rd(B[i]),T[B[i]]=i;
	int ans=0;
	REP(i,1,n){
		int res=query(T[A[i]]);
		chkmax(ans,res+1);
		add(T[A[i]],res+1);
	}
	printf("%d\n",ans);
	return 0;
}

T 3 T_3 T3——queue(3081)

Description:

有一棵树,现在有 m m m个操作,对于每个操作,有 2 2 2种:
1.有 x x x个人从根 1 1 1往下走(走的策略是该节点的编号最小且没有人),求最后一人在哪个节点。
2.将节点 x x x的人带走, x x x以上的节点的人会往下补充,求有多少人移动了。
n , m ≤ 1 0 5 n,m\le10^5 n,m105

Solution:

  • 首先对于一棵树,我们可以先将其转化到序列上,即 d f s dfs dfs序。
  • 并且,我们先将边的编号排序,这样保证最小。
  • 对于操作1,即为从后往前覆盖序列;
  • 对于操作2,即为修改一条链上的信息,以及求出其标记的链的长度。
  • 对于序列的覆盖以及修改,很容易想到线段树,而对于求标记的链长度,我们可以倍增跳。

Code

#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t) for(int i=(f),i##_end_=(t);i<i##_end_;++i)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;--i)
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
template<class T>inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
template<class T>inline void Rd(T &x){
    x=0;char c;
    while((c=getchar())<48);
    do x=(x<<1)+(x<<3)+(c^48);
    while((c=getchar())>47);
}

const int S=18,N=100005;

int n,m;
vector<int>E[N];
int Rt[N],sgID[N],tim;
int fa[N][S];
int sum[N<<2];
bool mark[N];
int ans;

#define lson L,mid,p<<1
#define rson mid+1,R,p<<1|1
void update(int L,int R,int p,int&x){
	if(!x or sum[p]==R-L+1) return;
	if(L==R){
		sum[p]=1;
		mark[L]=1;
		if(!--x) ans=L;
		return;
	}
	int mid=(L+R)>>1;
	update(lson,x),update(rson,x);
	sum[p]=sum[p<<1]+sum[p<<1|1];
}

void del(int L,int R,int p,int x){
	--sum[p];
	if(L==R) return;
	int mid=(L+R)>>1;
	if(x<=mid)del(lson,x);
	else del(rson,x);
}

void dfs(int x,int f){
	fa[x][0]=f;
	SREP(i,0,E[x].size()) if(E[x][i]!=f) dfs(E[x][i],x);
	sgID[Rt[x]=++tim]=x;
}

int main(){
//	freopen("queue.in","r",stdin);
//	freopen("queue.out","w",stdout);
	Rd(n),Rd(m);
	SREP(i,1,n){
		int a,b;
		Rd(a),Rd(b);
		E[a].push_back(b);
		E[b].push_back(a);
	}
	
	REP(i,1,n) sort(E[i].begin(),E[i].end());
	dfs(1,0);
	SREP(j,1,S) REP(i,1,n) fa[i][j]=fa[fa[i][j-1]][j-1];
	while(m--){
		int op,x;
		Rd(op),Rd(x);
		if(op==1){
			update(1,n,1,x);
			printf("%d\n",sgID[ans]);
		}
		else{
			ans=0;
			DREP(k,S-1,0) if(mark[Rt[fa[x][k]]]) x=fa[x][k],ans|=1<<k;
			mark[Rt[x]]=0;
			del(1,n,1,Rt[x]);
			printf("%d\n",ans);
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
NOI(全国青少年信息学奥林匹克竞模拟的测试数据是指用于评测参选手的程的输入和对应的输出。测试数据是非常重要的,因为它决定了参选手的程能否正确地解决问题。 在NOI模拟中,测试数据具有以下特点: 1.充分覆盖:测试数据应涵盖各种可能的输入情况,包括边界条件和极端情况。通过提供不同的测试数据,可以考察选手对问题的全面理解和解决能力。 2.随机性和均衡性:为了公平起见,测试数据应该是随机生成的,而不是针对某个特定算法或解法设计的。同时,测试数据应该是均衡的,即各种情况的概率应该大致相等,以避免偏向某些解法。 3.合理性和可行性:测试数据应该是合理和可行的,即符合题目要求的输入数据,并且是选手能够通过编写程来处理的。测试数据应该考虑到程的限制和时间复杂度,以充分测试选手的编程能力。 NOI模拟的测试数据通常由经验丰富的考题组负责生成。他们会根据题目的要求和限制,设计出一组合理、充分、随机和均衡的测试数据,以确保参选手的程在各种情况下都能正确运行,并且能通过性能测试。 总之,测试数据在NOI模拟中起到了至关重要的作用,它既考察了选手对问题的理解和解决能力,又提高了选手编程的技巧和效率。同时,合理和恰当的测试数据也是公平竞的保证,确保每个参选手有相同的机会和条件进行竞争。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值