2019.11.09日常总结兼onecode普及复赛模拟六题解

【前记】: 本日记部分内容选自老师讲解,若侵权,定删除。因为版权原因,部分内容不能公布。本题解仅仅作为参考,任何如何未经允许不得外传!!!

【题目A】:

【题意】: n n 个孩子,每个孩子都在读一本独特的书。在任何一天结束时,第 i i 个孩子将把他的书交给第 p i p_i 个孩子(如果 i = p i i = p_i ,则该孩子将把他的书交给他自己)。
保证 p i p_i 的所有值都是从 1 1 n n 的不同整数(即 p p 是一个排列)。序列 p p 每天都不变化,它是固定的。
例如,如果 n = 6 n = 6 p = [ 4 , 6 , 1 , 3 , 5 , 2 ] p = [4,6,1,3,5,2] ,则在第一天结束时,第一个孩子的书将属于第四个孩子,第二个孩子-第二个孩子将属于第六个孩子,依此类推。
在第二天结束时,第一个孩子的书将属于第三个孩子,第二个孩子将属于第二个孩子,依此类推。
您的任务是确定从 1 1 n n 的每个 i i ,第 i i 个孩子的书第一次返还给他所需的天数。
考虑以下示例: p = [ 5 , 1 , 2 , 4 , 3 ] p = [5,1,2,4,3] 。第一个孩子的书将传递给以下孩子:
第一天之后,它将属于第五个孩子, 第 2 2 天之后,它将属于第 3 3 个孩子, 第三天之后,它将属于第二个孩子, 在第 4 4 天之后,它将属于第一个孩子。
因此,第四天之后,第一个孩子的书将归还其所有者。第四天的书将在整整一天后第一次归还给他。
您必须回答 q q 个独立查询。
【思路】: 很简单的并查集练习题,答案即为每个数字所在集合的元素个数。在普通并查集的基础上,加一个数字表示集合内元素个数即可。
【仅仅无头文件的代码】:

const int N=2e5+100;
struct union_set{
	int f[N],s[N];
	void init(int n){
		for(int i=1;i<=n;i++){
			f[i]=i;s[i]=1;
		}
	}
	int getf(int x){
		if (f[x]==x) return x;
		return f[x]=getf(f[x]);
	}
	void merge(int x,int y){
		int a=getf(x),b=getf(y);
		if (a==b) return;
		if (s[a]<s[b]) swap(x,y);
		s[a]+=s[b];f[b]=a;
	}
	bool query(int a,int b){
		return getf(a)==getf(b);
	}
}F;
#define gc getchar()
#define g(c) isdigit(c)
inline int read(){
	char c=0;int x=0;bool f=0;
	while (!g(c)) f=c=='-',c=gc;
	while (g(c)) x=x*10+c-48,c=gc;
	return f?-x:x;
}
int test_number,n;
bool flag=true;
int main(){
	test_number=read();
	while (test_number--){
		n=read();F.init(n);
		if (flag) flag=false;
		else printf("\n");
		for(int i=1;i<=n;i++)
			F.merge(i,read());
		for(int i=1;i<n;i++)
			printf("%d ",F.s[F.getf(i)]);
		printf("%d",F.s[F.getf(n)]);
	}
	return 0;
}

【题目B】:

【题意】: 小明计划在一个 n n 层楼高的公寓买房。楼层从 1 n 1-n 。现在小明想知道从 1 1 层到其他各层的最小时间。
须知:对于 i ( 1 i n 1 ) a i i(1 \leq i\leq n-1),ai 表示从第 i i 层楼到第 i + 1 i+1 层楼爬楼所需时间; b i bi 表示从第 i i 层楼到第 i + 1 i+1 层楼电梯所需时间,另如果用电梯还需等待 c c 时间;
从第 x x 层楼到第 y ( x y ) y(x\neq y) 层楼,每次有两种方式:
如果爬楼梯,花费 a ( x ) + a ( x + 1 ) + a ( x + 2 ) + . . . + a ( y 1 ) a(x)+a(x+1)+a(x+2)+...+a(y-1) 时间。
如果乘电梯,花费 c + b ( x ) + . . . + b ( y 1 ) c+b(x)+...+b(y-1) 时间。
现在需要帮小明计算从 1 1 层到其他各层的最小时间。
【题意】: 考虑 d p dp ,显然一维 d p dp 肯定不行,所以我们考虑二维,记 d p [ i ] [ 0 ] dp[i][0] 表示从 1 1 i i 且最后一次为步行的最短时间, d p [ i ] [ 1 ] dp[i][1] 表示从 1 1 i i 且最后一次为乘电梯的最短时间。
【代码】:

const int N=2e5+100;
#define ll long long
ll f[N][2],c,a[N],b[N],n;
#define gc getchar()
#define g(c) isdigit(c)
inline ll read(){
	char c=0;ll x=0;bool f=0;
	while (!g(c)) f=c=='-',c=gc;
	while (g(c)) x=x*10+c-48,c=gc;
	return f?-x:x;
}
int main(){
	n=read();c=read();
	for(int i=1;i<n;i++)
		a[i]=read();
	for(int i=1;i<n;i++)
		b[i]=read();
	memset(f,127,sizeof(f));
	f[1][0]=0;f[1][1]=c;
	for(int i=2;i<=n;i++){
		f[i][0]=min(f[i-1][0],f[i-1][1])+a[i-1];
		f[i][1]=min(f[i-1][0]+c,f[i-1][1])+b[i-1];
	}
	printf("0");
	for(int i=2;i<=n;i++)
		printf(" %lld",min(f[i][0],f[i][1]));
	return 0;
}

【题目C】:

【题目】: 因篇幅问题, C C 题和 D D 题的题目略去。
【思路】: 二分答案,即二分 m i d mid 表示中位数是否可以为 m i d mid ,然后判定即可。注意判定需要分两部分:一为 m i d mid 是否有可能为中位数,二为当中位数为 m i d mid 时是否够钱。
【代码】:

const int N=2e5+1e3;
typedef long long ll;
ll s;int n,test_number;
struct node{
	ll l,r;bool flag;
	bool operator < (node p) const{
		return l<p.l;
	}
}a[N],b[N];
inline int check(ll mid){
	for(int i=1;i<=n;i++)
		a[i].flag=false;
	int cnt=0,tot=0;ll ans=0;
	for(int i=1;i<=n;i++){
		if (a[i].l>mid){
			cnt++;ans+=a[i].l;
			a[i].flag=true;
			if (cnt>n/2)
				return -1;
//			太多数字>mid,mid需要增大 
		}
		else if (a[i].r<mid){
			tot++;ans+=a[i].l;
			a[i].flag=true;
			if (tot>n/2)
				return -2;
//			太多数字<mid,mid需要减小 
		}
	}
	if (ans>s) return false;
	register int ret=0;
	for(int i=1;i<=n;i++)
		if (a[i].flag==false)
			b[++ret]=a[i];
	register bool flag=false;
	for(int i=1;i<=ret;i++){
		if (tot<n/2){
			ans+=b[i].l;
			tot++;
		}
		else ans+=mid;
	}
	return ans<=s;
//	0:ans>s,意味着mid需要减小
//	1:ans<=s,意味着mid可以增大 
}
#define gc getchar()
#define g(c) isdigit(c)
inline ll read(){
	char c=0;ll x=0;bool f=0;
	while (!g(c)) f=c=='-',c=gc;
	while (g(c)) x=x*10+c-48,c=gc;
	return f?-x:x;
}
ll l,r,mid,ans;
const ll inf=1e15;
int main(){
	test_number=read();
	while (test_number--){
		n=read();s=read();l=inf;r=0;
		for(int i=1;i<=n;i++){
			a[i].l=read();l=min(l,a[i].l);
			a[i].r=read();r=max(r,a[i].r);
		}
		sort(a+1,a+n+1);
		while (l<=r){
			mid=(l+r)>>1;
			int result=check(mid);
			switch(result){
				case -1:l=mid+1;break;
				case -2:r=mid-1;break;
				case  0:r=mid-1;break;
				default:ans=mid;l=mid+1;break;
			}
//			与众不同的二分…… 
		}
		printf("%d\n",ans);
	}
	return 0;
}

【题目D】:

【思路】: 离散化长方形的坐标,然后统计有多少个联通块即可,注意一定要按代码写,其它代码可能会超时!
【代码】:

int test_number,ans;
#define gc getchar()
#define g(c) isdigit(c)
inline int read(){
	char c=0;int x=0;bool f=0;
	while (!g(c)) f=c=='-',c=gc;
	while (g(c)) x=x*10+c-48,c=gc;
	return f?-x:x;
}
bool b[30][30];
struct node{
	int x,y;
};
const int dx[]={1,0,-1,0};
const int dy[]={0,1,0,-1};
inline void bfs(int bx,int by){
	queue<node> q;
	q.push((node){bx,by});
	b[bx][by]=false;ans++;
	while (q.size()){
		node z=q.front();q.pop();
		for(int i=0;i<4;i++){
			int x=z.x+dx[i],y=z.y+dy[i];
			if (x<0||x>20||y<0||y>20) continue;
			if (!b[x][y]) continue;
			q.push((node){x,y});
			b[x][y]=false;
		}
	}
}
struct data{
	int x,rq,id;
}a[10];
bool cmp1(data a,data b){
	return a.x<b.x;
}
bool cmp2(data a,data b){
	return a.id<b.id;
}
int main(){
	test_number=read();
	while (test_number--){
		for(int i=1;i<9;i++){
			a[i].x=read();
			a[i].id=i;
		}
		sort(a+1,a+9,cmp1);
		a[1].rq=1;
		for(int i=2;i<9;i++)
			if (a[i].x!=a[i-1].x)
				a[i].rq=a[i-1].rq+2;
			else a[i].rq=a[i-1].rq;
		sort(a+1,a+9,cmp2);
		memset(b,1,sizeof(b));
		for(int i=a[1].rq;i<=a[3].rq;i++)
			b[i][a[2].rq]=b[i][a[4].rq]=0;
		for(int j=a[2].rq;j<=a[4].rq;j++)
			b[a[1].rq][j]=b[a[3].rq][j]=0;
		for(int i=a[5].rq;i<=a[7].rq;i++)
			b[i][a[6].rq]=b[i][a[8].rq]=0;
		for(int j=a[6].rq;j<=a[8].rq;j++)
			b[a[5].rq][j]=b[a[7].rq][j]=0;
			//相当于真的把篱笆给画出来
		for(int i=0;i<21;i++)
			for(int j=0;j<21;j++)
				if (b[i][j])
					bfs(i,j);
		printf("%d\n",ans);
		ans=0;
	}
	return 0;
}
发布了82 篇原创文章 · 获赞 4 · 访问量 1788

猜你喜欢

转载自blog.csdn.net/ZHUYINGYE_123456/article/details/102991440