CometOJ欢乐赛总结

今天下午尝试了下CometOJ的欢乐赛,好吧,是对那些大佬来说是欢乐赛,其中出了9道题,5个小时,我只A了2道,其中有三道都是超时,所以最后也没做出了,关于题目,比赛完只搞定了其中5题,觉得是自己比赛应该可以搞定的。

B 距离产生美

在这里插入图片描述
这道签到题我也花了半个小时(黑脸),这道其实关键在于选择合适的贪心算法,就是每次更新变量的时候需要判断它前后两个变量的值,我们的贪心思想是:尽量选择更新中间那个变量,这样可以使得所需要更新得变量尽可能地少,即是题目要求

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
const long long inf = 1e18;
inline int read(){
    char ch = getchar(); int x = 0, f = 1;
    while(ch< '0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int main(){
	int n=read(),k=read();
	long long a[maxn];
	int ans = 0;
	for(int i=0;i<n;i++)
		a[i] = read();
	for(int i=1;i<n;i++){
		if(fabs(a[i]-a[i-1]) < k){
			if(fabs(a[i+1]-a[i])<k){
				a[i] = inf;
				ans++;
			}else{
				a[i-1] = inf;
				ans++;
			}
		}
	}
	printf("%d\n",ans);
	return 0;
}

C 烤面包片

在这里插入图片描述
这道题原来我以为卡的是时间和数据量,对于阶乘来说1e9的数据是真的太大了,这样的一般复杂度是O(1),但比赛的时候我没有反应过来,超时了一直以为是算法不够优化。尝试各种算法,包括二分求阶乘,还有通过二进制来计算大数据量,像下面这个,两数相乘,通过将其中一个数化为二进制,利用按位与来对数相乘不断取模,从而不会爆数据。
在这里插入图片描述
其中还想到用二分递归,但数据量真的太大啦,这点优化完全不济于事。所以输入大的数据的会发现出现程序动不了了。
在这里插入图片描述
这题的真正解决思路是:首先根据范围可得 4!!一定大于109 (事实上甚至大于1018),所
以 4!!!一定能被 mod 整除(n>4 也同理)。那么我们只需要判断 n≤3
的情况了。直接计算即可。如果写一个阶乘函数,然后调用连续三次
来获得答案,如:((n!%mod)!%mod)!%mod 可能会无法通过以下的 text:
2 2
(本应输出 0,但三次调用阶乘函数最终会输出 1,因为第一次调用
得到 0,之后再调用阶乘函数发现 0!是 1)

一个较好的处理方式是将 n=0,1,2 特判掉,然后预先计算出 3!!为
720,然后用一次 for 循环解决这道题。

#include<bits/stdc++.h>
using namespace std;
int main(){
	long long n,mod,ans=1;
	cin >> n >> mod;
	if(n==0)
		return cout << 1%mod,0;
	if(n<3)
		return cout << n%mod,0;
	if(n>3)
		return cout << 0,0;
	for(int i=1;i<=720;i++)
		ans = (ans*i)%mod;
	return cout << ans,0;
}

G 篮球校赛

在这里插入图片描述
这道题比赛的时候没有用任何特殊的操作或者是剪枝就直接递归,虽然能算出答案,但导致程序计算时计算了很多没有用的量,耗费了大量的时间,然后我就卡在时间上,比赛过后,看着大佬的题解:
大佬是在存入数据的时候就按能力值从1-5排序更新每一个位置,并把这些数据存入到一个二维数组中,在调用dfs递归的时候,就根据这个二维数组进行递归和计算,节省了很多时间,但有些人说可以用五次循环直接暴力得出,这个我还没有尝试。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+2;
const int N = 5;
ll a[maxn][N+1],ans=0;
bool vis[maxn];//记录是否取某个球员
ll mmax[N+1][N+1];
void dfs(int cur,ll temp){
	if(cur == N+1){
		ans = max(ans,temp); 
		return;
	}
	for(int i=1;i<=N;i++){
		if(vis[mmax[i][cur]])
			continue;
		vis[mmax[i][cur]] = true;
		dfs(cur+1,temp+a[mmax[i][cur]][cur]);
		vis[mmax[i][cur]] = false;
	}
}
int main(){
	std::ios::sync_with_stdio(false); 
	int n;
	cin >> n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=N;j++){
			cin >> a[i][j];
			for(int pos=1;pos<=N;pos++){ // pos在该能力的排位 
				if(a[i][j] > a[mmax[pos][j]][j]){
					for(int k=N;k>=pos+1;k--)
						mmax[k][j] = mmax[k-1][j];//将这些元素后移一位
					mmax[pos][j] = i;//插入
					break;
				}
			}
		}
	}
	dfs(1,0);
	cout << ans << endl;
	return 0;
}

G 三元组

在这里插入图片描述
出题人思路:对于一组满足条件的(i,j):
2 ∗ min(?? + ??,?? + ??) ≤ max(?? + ??,?? + ??)
也必然满足:
min(ai + aj,?? + ??) ≤ max(?? + ??,?? + ??)
不妨设 2(?? + ??) ≤ ?? + ??,即ai + aj ≤ ?? + ??
此时,显然不需要考虑min(?? + ??,?? + ??) = ?? + ??这个条件
移项可得 (2 ∗ ?? − bi) + (2 ∗ ?? − bj) ≤ 0
直接按照2 ∗ ?? − ??升序进行排序,记数组为P
此时可以发现性质:
若??最多只能和? ?匹配,则?? + 1只能和[i + 1,j]之间的匹配
此时使用滑窗即可计算答案
又因为我们只考虑了2(?? + ??) ≤ bi + bj的情况
再对每个三元组中的??,bi进行交换,计算一次答案即可。
时间复杂度O(n + nlog2 ?)

//三元组 
#include<bits/stdc++.h>
using namespace std;
typedef long long ll; 
const int maxn = 2e5+10;
const ll mod = 1e9+7;
int n;
ll sum[maxn];
struct node{
	ll a,b,c;
}p[maxn];

bool cmp(node x,node y){
	return (2*x.a-x.b) < (2*y.a-y.b);
}//比较函数 

ll solve(){
	sort(p+1,p+n+1,cmp);
	sum[0] = 0;
	for(int i=1;i<=n;i++)
		sum[i] = (sum[i-1]+p[i].c)%mod;//求前缀和 
	int l=1,r=n,sign=0;
	ll k = 2*p[1].a - p[1].b;
	while(l <= r){
		int mid = (l+r)>>1;
		ll t = 2*p[mid].a - p[mid].b;
		if((t+k) <= 0)
			sign = mid,l = mid+1;
		else
			r = mid-1;
	}//求出最右边下标
	ll res = 0;//two pointer
	for(l=1,r=sign;l<=r;l++){
		while(l<=r && (2ll*p[l].a + 2ll*p[r].a - p[l].b - p[r].b) > 0)
			r--;
		if(l > r)
			break;
		ll temp = (sum[r]-sum[l-1]+mod)%mod;
		res = (res + p[l].c * temp % mod)%mod;
	}
	return res;
}

int main(){
	std::ios::sync_with_stdio(false);
	cin >> n;
	for(int i=1;i<=n;i++)
		cin >> p[i].a >> p[i].b >> p[i].c;
	ll ans = solve();
	for(int i=1;i<=n;i++)
		swap(p[i].a,p[i].b);
	ans += solve();
	ans %= mod;
	cout << ans << endl;
	return 0;
}	

I: Gree 的心房

在这里插入图片描述
这道签到题也卡了我一会,原来是数据量卡了我,没有用long long,最后几个数据过不了,这题的思路就是求最短路,关于最短路,在这里我们可以知道沿着边缘从左上角走到右下角最短,即(n-1)+(m-1),然后关于障碍物,当取得最短路的时候,障碍物最多可以放(n-1)(m-1)个,因为障碍物是随机放的,也没要求我们输出障碍物位置,我们就可以直接判断,其负责度为O(1)。
#include<bits/stdc++.h>
using namespace std;
int main(){
long long n,m,k;
scanf("%lld%lld%lld",&n,&m,&k);
long long ans = n+m-2;
if(k > (n*m)-ans-1)
printf("-1");
else
printf("%lld",ans);
return 0;
}

题外话

今天还是很自闭的,毕竟一直卡着,5个小时只做出2题,哎,还是自己做的题目不够多,太菜了。也感觉最近的生活有些累,还是得好好缓一下,加油!晚安。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值