华农新生赛补题(补完啦)

目录

阿杰的粉红色气球:

Rune与多项式多点求值:

神奇的青蛙:

被诅咒的鸽手:

建设咕咕岛:


​​​​​​​

阿杰的粉红色气球:

题意:要求是行有序以及列有序,需要求出行有序所需要的交换次数,同时需要找到列有序的交换次数,根据贪心策略,如果直接暴力的话,就枚举位置,然后遍历这个循环,找到这个位置该有的大小,然后交换,这样复杂度是On^2)明显会tle,这题用map会卡常,但如果用二分就没有问题

		for(i=1;i<=n;i++){
			int l = 1,r = n;
			if(a[i]!=x[i]){
				while(l<r){
					int mid = (l+r)/2;
					if(a[mid]>=x[i]){//mid要放在排好序的里面
						r = mid;
					}
					else{
						l = mid + 1;
					}//13456
				}
				int v = x[l];
//64513//34516 13456 64513--> 34516 --> 43516 --> 54316 --> 14356
				x[l] = x[i];
				x[i] = v;
				cnt1++;
				i--;
			}
		}

二分找到需要的那个数(因为排序之后是单调,然后就可以找到),找到那个位置之后就开始交换,但我这里是用排序好的去做二分,所以找到的不一定是我需要的,小于a[mid]的有很多个,然后花最大O(n*logn)的次数去找,如果运气好,一次就找到了,运气不好,最多也就O(logn)

左部分和右部分都这样操作,最后再取一个min,就可以得到答案 

ac代码:

#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn = 5e5+10;
int a[maxn],b[maxn],x[maxn],y[maxn];//ab是排序后的,xy是排序前的
int main() {
	int t,i;
	int n;
	int cnt1,cnt2;
	cin>>t;
	while(t--){
		cnt1=0,cnt2=0;
		cin>>n;
		for(i=1;i<=n;i++){
			cin>>a[i];
			cin>>b[i];
			x[i]=a[i];
			y[i]=b[i];
		}
		sort(a+1,a+n+1);
		sort(b+1,b+n+1);
		//我得到了两对不一样的数组,现在需要对其中一对进行排序,然后得到他们的大小,然后搜索第一个数
		for(i=1;i<=n;i++){
			int l = 1,r = n;
			if(a[i]!=x[i]){
				while(l<r){
					int mid = (l+r)/2;
					if(a[mid]>=x[i]){//mid要放在排好序的里面
						r = mid;
					}
					else{
						l = mid + 1;
					}//13456
				}
				int v = x[l];//64513//34516 13456 64513--> 34516 --> 43516 --> 54316 --> 14356
				x[l] = x[i];
				x[i] = v;
				cnt1++;
				i--;
			}
		}
		for(i=1;i<=n;i++){
			int l = 1,r = n;
			if(b[i]!=y[i]){
				while(l<r){
					int mid = (l+r)/2;
					if(b[mid]>=y[i]){
						r = mid;
					}
					else{
						l = mid + 1;
					}
				}
				int v = y[l];
				y[l] = y[i];
				y[i] = v;
				cnt2++;
				i--;
			}
			//l就是我找到的东西的位置,将两个位置交换,然后cnt++;
		}
		int ans = min(cnt1,cnt2);
		cout<<ans<<endl;
	}
	return 0;
}

Rune与多项式多点求值:

题意:给你两个数字,每一位任意组合组成新的数,这个数%6等于另一个数就可以,只要两个的答案里面有一个相等就没有问题。

题解:

经过分析可以知道,十位数,百位数,千位数,任何一个%6,得到的都是4,也就是说,除了个位数之外的所有数取模都得到4,只是看4有多少倍罢了,那么可以把输入的数当字符处理了,然后枚举每一个位数当个位数的时候((s-k)*4+k) % 6,用这个公式来计算,时间复杂度是O(n)

ac代码:

注意一开始要给答案初始化一下

//#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
using namespace std;
const int maxn = 1e5+10;
//char a[maxn],b[maxn];
//long long a,b;
char a[maxn],b[maxn];
int ans1[maxn],ans2[maxn];
int main() {
	int t;
	scanf("%d",&t);
	while(t--){
		int sum1=0,sum2=0,i,flag=0,lengtha=0,lengthb=0;
		scanf("%s%s",a,b);
		memset(ans1, 0, sizeof(ans1));
		memset(ans2, 0, sizeof(ans2));
		int lena = strlen(a);
		int lenb = strlen(b);
		for(i=0;i<lena;i++){
			sum1 += a[i] - '0';
//			printf("%d",sum);
//			lengtha++;
		}
		for(i= 0;i<lena;i++){
			int k = a[i] - '0';
			ans1[i] = ((sum1-k)*4+k)%6;
//			printf("%d",ans1[i]);
		}
		for(i=0;i<lenb;i++){
			sum2 += b[i] - '0';
//			lengthb++;
		}
		for(i= 0;i<lenb;i++){
			int k = b[i] - '0';
			ans2[i] = ((sum2-k)*4+k)%6;
		}
		int j;
		sort(ans1,ans1+lena);
		sort(ans2,ans2+lenb);
		
		for(i=0;i<lena;i++){
//			for(j=0;j<b.length();j++){
//				if(ans1[i]==ans2[j]){
//					flag=1;
//					break;
//				}
//			}
			int l = 0,r=lenb;
			while(l<=r){
				int mid = (l+r)/2;
				if(ans2[mid]>ans1[i]){
					r = mid -1;
				}else if(ans2[mid]<ans1[i]){
					l = mid + 1;
				}
				else{
					flag=1;
					break;
				}
		
			}
		}
		if(flag==1){
			printf("Yes\n");
		}else{
			printf("No\n");
		}
	}
	return 0;
}

神奇的青蛙:

题意:给定一个n*n的方阵,然后农夫去抓青蛙,抓到第一只青蛙开始按自己的质量开始变大,质量小的青蛙会吃掉质量大的青蛙,让农夫找到手上青蛙可能的最大质量。

题解:

首先沿着斜线,即对角线进行遍历,找到质量最大的那一个,在这些对角线选出的最大元素中进行O(n^2)次数的遍历,然后得到答案。

struct wa{
	ll x,y;
	ll value;
};

因为青蛙有三个参数,所以使用一个结构体来存储答案

for(k=1;k<=n;k++){
		ll l = 1,r = k;
		wa ans;
		ll init=0;
		init = wawa[l][r];
		while(l<=n&&r<=n&&l>=1&&r>=1){
			if(wawa[l][r]>=init){
				init = wawa[l][r];
				ans.x = l;
				ans.y = r;
				ans.value = init;
			}
			l++,r--;
		}
		ans_true.push_back(ans);
	}

此处是沿着对角线进行遍历寻找最大值,至于是哪个对角线,可以看图

​​​​​​​

就是图中表示的这一部分

for(k=1;k<=n;k++){
		ll l = k,r = 1;
		wa ans;
		ll init;
		init = wawa[l][r];
		while(l<=n&&r<=n&&l>=1&&r>=1){
			if(wawa[l][r]>=init){
				init = wawa[l][r];
				ans.x = l;
				ans.y = r;
				ans.value = init;;
			}
			l++,r++;
		}
		ans_true.push_back(ans);
	}

 代码后面几个部分理解的图按顺序如下

 

 ac代码:

#include <stdio.h>
#include <vector>
#include <cmath>
#include <math.h>
using namespace std;
const int maxn = 3e3 + 10; 
typedef long long ll;
/*

先构建一个结构一寸青蛙
然后每一行遍历,遍历之后找到最大值存进去
最后再for找到这个所谓的最大值
*/
ll wawa[maxn][maxn];

int getabs(ll x){
	if(x>=0) return x;
	else return -x;
}
 
struct wa{
	ll x,y;
	ll value;
};

vector<wa> ans_true;

int main() 
{
	ll n,i,j,k;
	scanf("%lld",&n);
	for(i=1;i<=n;i++){
		for(j=1;j<=n;j++){
			scanf("%lld",&wawa[i][j]);
		}
	}
	//从左向右下对角线每个都找
	for(k=1;k<=n;k++){
		ll l = 1,r = k;
		wa ans;
		ll init=0;
		init = wawa[l][r];
		while(l<=n&&r<=n&&l>=1&&r>=1){
			if(wawa[l][r]>=init){
				init = wawa[l][r];
				ans.x = l;
				ans.y = r;
				ans.value = init;
			}
			l++,r--;
		}
		ans_true.push_back(ans);
	}
	//从右向左下对角线每个都找
	for(k=1;k<=n;k++){
		ll l = k,r = 1;
		wa ans;
		ll init;
		init = wawa[l][r];
		while(l<=n&&r<=n&&l>=1&&r>=1){
			if(wawa[l][r]>=init){
				init = wawa[l][r];
				ans.x = l;
				ans.y = r;
				ans.value = init;;
			}
			l++,r++;
		}
		ans_true.push_back(ans);
	}
	for(k=1;k<=n;k++){
		ll l = k,r = n;
		wa ans;
		ll init;
		init = wawa[l][r];
		while(l<=n&&r<=n&&l>=1&&r>=1){
			if(wawa[l][r]>=init){
				init = wawa[l][r];
				ans.x = l;
				ans.y = r;
				ans.value = init;;
			}
			l++,r--;
		}
		ans_true.push_back(ans);
	}
	for(k=1;k<=n;k++){
		ll l = 1,r = k;
		wa ans;
		ll init;
		init = wawa[l][r];
		while(l<=n&&r<=n&&l>=1&&r>=1){
			if(wawa[l][r]>=init){
				init = wawa[l][r];
				ans.x = l;
				ans.y = r;
				ans.value = init;;
			}
			l++,r++;
		}
		ans_true.push_back(ans);
	}
	ll cnt = ans_true.size();
	ll an=0;
	for(i=0;i<cnt-1;i++){
		for(j=i+1;j<cnt;j++){
			an = max(an,min(ans_true[i].value,ans_true[j].value)*(getabs(ans_true[i].x-ans_true[j].x)+getabs(ans_true[i].y-ans_true[j].y)));
//			printf("%lld\n",an);

		}
	}
	printf("%lld",an);
	return 0;

	//
}

被诅咒的鸽手:

题意:给你一个公式,让你找到其中最大的y使得其小于k

题解:

首先这题的思路就是找到y,然后带入公式,不断遍历,找到最大的,很显然,如果你一个一个的枚举,不tle才怪,所以不能枚举。要找一个数,二分嘛,然后二分主要是看条件,用<=k的板子,但是也不是万事大吉了,需要用放缩确定一下右边界,不然可能会溢出,搞的非常难受。

推倒如图:

 然后里面就有两个部分非常难以处理,第一个是y的阿尔法次方,这个需要用快速幂

ll ksm(ll a,ll b){
	ll ans = 1;
	for(;b>0;b /=2 , a*= a){
		if(b % 2 != 0){
			ans *= a;
		}
	}
	return ans;
}

另一个部分就是[logy],那就向上求大值就可以了,2^x>y求x

ll two(ll a){
	ll init =1;
	ll cnt=0;
	for(int i=1;i<=a;i++){
		if(init>=a){
			break;
		}
		init *= 2;
		cnt++;
	}
	return cnt;
}

ac代码:

#include <iostream>
#include <stdio.h>
#include <cmath>
using namespace std;
#define rep(i,j,n) for(int i=j;i<=n;i++);
typedef long long ll;
//题意:给你一个公式,让你找到其中最大的y使得其小于k
/*
由于K值很大,若使用暴力方法则会超时,因此我们考虑对二分查找n的值,并代入式中判断。

对于log2(γ)向上取整,我们只需要依次枚举2的指数,找到第一个使得2x>γ的值x即可。(最多不会超过64次)

两式相乘,我们单独处理两个式子,先求γα,因为βlog2γ必定≥1,所以若是γα>k,则必不成立,直接返回。

求出βlog2γ后将它与k/γα相比,否则可能超出范围。

题解:
首先这题的思路就是找到y,然后带入公式,不断遍历,找到最大的,很显然,如果你一个一个的枚举,不tle才怪,所以不能枚举。要找一个数,二分嘛,然后二分主要是看条件,用<=k的板子,但是也不是万事大吉了,需要用放缩确定一下右边界,不然可能会溢出,搞的非常难受。
然后里面就有两个部分非常难以处理,第一个是y的阿尔法次方,这个需要用快速幂
另一个部分就是[logy],那就向上求大值就可以了,2^x>y求x
*/
ll ksm(ll a,ll b){
	ll ans = 1;
	for(;b>0;b /=2 , a*= a){
		if(b % 2 != 0){
			ans *= a;
		}
	}
	return ans;
}
ll two(ll a){
	ll init =1;
	ll cnt=0;
	for(int i=1;i<=a;i++){
		if(init>=a){
			break;
		}
		init *= 2;
		cnt++;
	}
	return cnt;
}
//ll two(ll a){
//	int ans = 0;
//	if((a&-a)==a){
//		ans--;
//	}
//	while(a>0){
//		ans++;
//		a/=2;
//	}
//	return ans;
//}

int main() {
	ll t,a,b,k,y;//
	scanf("%lld",&t);
	while(t--){
		scanf("%lld%lld%lld",&a,&b,&k);
		ll l = 1, r = pow(k/b+1,1.0/a);
		//用二分的目的就是查找到一个能够满足的y,如果枚举y的话一定会tle,现在需要考虑的就是我到底怎么二分才可以找到需要的y
		while(l<r){
			ll mid =(l+r+1)/2;
			if(ksm(mid,a)*b<=k/two(mid)){
				l = mid;
			}else{
				r = mid - 1; 
				
			}
		}
		printf("%lld\n",r);
	}
	return 0;
}

建设咕咕岛:

题意:求建仓和不建仓库合起来的花费的最小值

题解:

动态规划,

首先排序自然不用说了,然后动态规划的本质就是保留前面的状态在后面进行使用。

用了一个二维数组来表示,1这一行表示建立仓库的最小值,0这一行表示不建立仓库的最小值,然后往回遍历,每次都找,前面的状态直接留存

​​​​​​​ac代码:

//#include <iostream>
#include <stdio.h>
#include "cmath"
#include <algorithm>
using namespace std;
typedef long long ll;
const ll inf = 1e15 + 10;
//房屋有两个属性,可以建立一个结构体来考虑这个事情
struct house{
	ll poi,price;
}arr[3010];
//cmp按位置排序
bool cmp(struct house a,struct house b){
	return a.poi<b.poi;
}

//想要挖地道的居民只能选择去西边离自己最近的粮仓借取物资
//二维数组来dp
ll dp[3010][10];
int main() {
	ll t,n,d,i,j,mincost,dis;
	scanf("%lld",&t);
	while(t--){
		scanf("%lld%lld",&n,&d);
		for(i=1;i<=n;i++){
			scanf("%lld%lld",&arr[i].poi,&arr[i].price);
		}
		//排序不要排错了
		sort(arr+1,arr+n+1,cmp);
		dp[1][1]=arr[1].price;
		dp[1][0]=arr[1].price+1;
		for(i=2;i<=n;i++){
			dis = 0;
			mincost=inf;
			//第一种情况,建立仓库
			//1这个位置都是寸仓库的位置
			//这里并不是表示建立仓库的时候,前面都建立仓库,只是说这个位置建立了仓库,还要看前面的最优解,又可能前面的最优解是不建立仓库的
			dp[i][1]=min(dp[i-1][1],dp[i-1][0])+arr[i].price;
			//下面这种情况就是不建仓库的
			for(j=i-1;j>=1;j--){
				dis += (i - j)*(arr[j+1].poi - arr[j].poi);
				mincost = min(mincost,dp[j][1]+dis*d);
			}
			dp[i][0]=mincost;
		}
		printf("%lld\n",min(dp[n][0],dp[n][1]));
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值