第十三届山东省大学生程序设计竞赛

第十三届山东省大学生程序设计竞赛

A. Orders

解题思路:

对订单进行升序排序。

遍历每一天,我们每天生成k件货物,到第i天就减去需要的,不够就是No

代码:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
typedef pair<ll,ll> pii;
#define fi first
#define se second
int n,m,k;
void solve()
{
	scanf("%d %d",&n,&k);
	vector<pii> v(n + 1);
	for(int i = 1;i<=n;i++)
	{
		scanf("%lld %lld",&v[i].fi,&v[i].se);
	}
	sort(v.begin()+1,v.end());
	ll sum = 0;
	ll last = 0;
	for(int i = 1;i<=n;i++)
	{
		sum += (v[i].fi - last) * k;
		last = v[i].fi;
		if(sum >= v[i].se)
		{
			sum -= v[i].se;
		}
		else
		{
			printf("No\n");
			return;
		}
	}
	printf("Yes\n");
	
}

int main()
{
	int t = 1;
	scanf("%d",&t);
	while(t--)
	{
		solve();
	}
	return 0;
	
}

I. Three Dice

解题思路:

直接暴力分类讨论即可。

代码1:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
typedef pair<ll,ll> pii;
#define fi first
#define se second
int n,m,k;
void solve()
{
	ll a,b;
	scanf("%lld %lld",&a,&b);
	vector<int> black;
	black.push_back(2);
	black.push_back(3);
	black.push_back(5);
	black.push_back(6);
	map<int,bool> q;
	n = black.size();
	for(int i = 0;i<n;i++)
	{
		for(int j = 0;j<n;j++)
		{
			for(int k = 0;k<n;k++)
			{
				int res = black[i] + black[j] + black[k];
				q[res] = true;
			
			}
		}
	}
	map<int,int> s;
	for(int i = 0;i<n;i++)
	{
		for(int j = 0;j<n;j++)
		{
			int res = black[i] + black[j];
			s[res] = 1;
		}
	}
	if(a == 0)
	{
		for(auto c : q)
		{
			if(c.fi == b)
			{
				puts("Yes");
				return;
			}
		}
		puts("No");
		return;
	}
	else if(a == 1)
	{
		for(auto c : s)
		{
			if(c.fi == b)
			{
				puts("Yes");
				return;
			}
		}
		puts("No");
		return;
	}
	else if(a == 4)
	{
		for(auto c : s)
		{
			if(c.fi == b)
			{
				puts("Yes");
				return;
			}
		}
		puts("No");
		return;
	}
	else if(a == 2)
	{
		if(b == 2 || b == 3 || b == 5 || b == 6)
		{
			puts("Yes");
			return;
		}
		else
		{
			puts("No");
			return;
		}
	}
	else if(a == 5)
	{
		if(b == 2 || b == 3 || b == 5 || b == 6)
		{
			puts("Yes");
			return;
		}
		else
		{
			puts("No");
			return;
		}
	}
	else if(a == 8)
	{
		if(b == 2 || b == 3 || b == 5 || b == 6)
		{
			puts("Yes");
			return;
		}
		else
		{
			puts("No");
			return;
		}
	}
	else if(a == 3)
	{
		if(b != 0)
		{
			puts("No");
			return;
		}
		else
		{
			puts("Yes");
			return;
		}
	}
	else if(a == 12)
	{
		if(b != 0)
		{
			puts("No");
			return;
		}
		else
		{
			puts("Yes");
			return;
		}
	}
	puts("No");
	
}

int main()
{
	int t = 1;
//	scanf("%d",&t);
	while(t--)
	{
		solve();
	}
	return 0;
	
}

优雅的题解思路代码(推荐):

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
typedef pair<ll,ll> pii;
#define fi first
#define se second
int n,m,k;
int a[] = {0,1,0,0,4,0,0};
int b[] = {0,0,2,3,0,5,6};
void solve()
{
	scanf("%d %d",&n,&m);
	for(int i = 1;i<=6;i++)
	{
		for(int j = 1;j<=6;j++)
		{
			for(int k = 1;k<=6;k++)
			{
				if(a[i] + a[j] + a[k] == n && b[i] + b[j] + b[k] == m)
				{
					puts("Yes");
					return;
				}
			}
		}
	}
	puts("No");
	
}

int main()
{
	int t = 1;
//	scanf("%d",&t);
	while(t--)
	{
		solve();
	}
	return 0;
	
}

G. Matching

解题思路:

公式变换:

(1)ij=aiaj(2)aii=ajj

不难发现,aii相同的可以分到同一集合中。且任一集合中的边都不会指向另一个集合。

所以,我们每次选取一个集合中ai最大的两个点即可。

代码:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
typedef pair<ll,ll> pii;
#define fi first
#define se second
int n,m,k;
void solve()
{
	scanf("%d",&n);
	vector<ll> a(n + 1);
	map<ll,vector<ll>> q;
	for(int i = 1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		ll res = a[i] - i;
		q[res].push_back(a[i]);
	}
	ll ans = 0;
	for(auto &t : q)
	{
		vector<ll> &v = t.se;
		sort(v.begin(),v.end());
		for(int i = v.size()-1;i > 0;i--)
		{
			ll a = v[i];
			ll b = v[i-1];
			if(a + b > 0)
			{
				ans += a + b;
				i --;
			}
		}
	}
	printf("%lld\n",ans);
	
}

int main()
{
	int t = 1;
	scanf("%d",&t);
	while(t--)
	{
		solve();
	}
	return 0;
	
}

D. Fast and Fat

解题思路:

  1. 二分队伍速度x.

  2. 对于速度大于等于x的队员,他们的极限承重能力为wj,我们可进行公式推导:

    (3)vi(wjwi)=x(4)viwj+wi=x(5)vi+wix=wj(6)wj=vi+wix

    所以,我们可以记录可背人队员的承重极限,然后和速度小于x的队员进比较,分配任务。

    贪心地想,从最轻的落后队员开始看,尽量让承重极限小的队员背他,以此类推。

代码:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
typedef pair<ll,ll> pii;
#define fi first
#define se second
int n,m,k;
void solve()
{
	scanf("%d",&n);
	vector<ll> v(n + 1),w(n + 1);
	vector<pair<ll,int>> a(n + 1);
	vector<pair<pair<ll,ll>,int>> q(n + 1);
	for(int i = 1;i<=n;i++)
	{
		scanf("%lld %lld",&v[i],&w[i]);
		a[i] = {v[i] + w[i],i};
		q[i] = {{w[i],v[i]},i};
	}
	sort(q.begin() + 1,q.end());
	sort(a.begin() + 1,a.end());
	int l = 0;
	int r = 1e9 + 1;
	auto check = [&](int x)
	{
		
		vector<int> t,ans;
		vector<bool> st(n + 1);
		for(int i = 1;i<=n;i++)
		{
			if(q[i].fi.se < x)
			{
				st[q[i].se] = true;
				t.push_back(q[i].fi.fi);
			}
		}
		for(int i = n;i>=1;i--)	
		{
			if(!st[a[i].se])
			{
				ans.push_back(a[i].fi - x);
			}
		}
		reverse(ans.begin(),ans.end());
		if(ans.size() < t.size())
		{
			return false;
		}
		int idx = ans.size()-1;
		for(int i = t.size()-1;i>=0 && idx >= 0;i--)
		{
			while(idx >= 0 && ans[idx] < t[i])
			{
				idx --;
			}
			if(idx >= 0)
			{
				idx --;
				t[i] = 0;
			}
			else
			{
				break;
			}
		}
		if(t.empty() || t[0] == 0)
		{
			return true;
		}
		return false;
	};
	while(l + 1 < r)
	{
		int mid = l + r >> 1;
//		cout<<mid<<endl;
		if(check(mid))
		{
			l = mid;
		}
		else
		{
			r = mid;
		}
	}
	printf("%d\n",l);
	
	
}

int main()
{
	int t = 1;
	scanf("%d",&t);
	while(t--)
	{
		solve();
	}
	return 0;
	
}

L. Puzzle: Sashigane

解题思路:

不难发现,我们围着块周围铺全覆盖的L型块即可。

一共n层,开头有了一层,我们还需要铺n1层。

自己画图试一下就好。

代码:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using i128 = __int128;
typedef pair<ll,ll> pii;
#define fi first
#define se second
 

void solve()
{
	int n,a,b;
	scanf("%d %d %d",&n,&a,&b);
	int u = a;
	int d = a;
	int l = b;
	int r = b;
	printf("Yes\n%d\n",n-1);
	for(int i = 1;i <= n - 1;i++)
	{
//		cout<<i<<endl;
		//Èç¹ûÑ¡(u-1,l-1) 
		if(u > 1 && l > 1)
		{
			u --;
			l --;
			printf("%d %d %d %d\n",u,l,d - u, r - l);
		}
		else if(u > 1 && r < n)
		{
			u --;
			r ++;
			printf("%d %d %d %d\n",u,r,d - u,l - r);
		}
		else if(d < n && l > 1)
		{
//			cout<<1<<endl;
			d ++;
			l --;
			printf("%d %d %d %d\n",d,l,u - d,r - l);
		}
		else if(d < n && r < n)
		{
			d ++;
			r ++;
			printf("%d %d %d %d\n",d,r,u - d,l - r);
		}
//		printf("udlr : %d %d %d %d\n",u,d,l,r);
	}
}

int main()
{
	int t = 1;
//	scanf("%d",&t);
	while(t--)
	{
		solve();
	}
	return 0;
	
}

E. Math Problem

解题思路:

首先,我们能够发现如果我们执行一次a操作,然后再执行一次b操作,那么除了花费增加了a+b,值没有任何变化。所以,如果我们能得到一个答案,那么一定是先将需要操作的b操作完,再进行a操作。

不难发现,如果我们一直进行b操作,那么n会在logkn级的操作次数内变为0。也就是说,我们可以枚举分别执行了0,1,2...log2nb操作,然后在执行完了ib操作后开始不断执行a操作直到求得答案。

如果我们在执行完了ib操作后有执行了ja操作,那么此次求得答案的花费就是ia+jb

那么执行a操作的次数会不会超时呢?

公式推导:

(7)f0=n(8)fi=kfi1+x,(0x<k,i>0)

暴力带入x=k+1看上界规律:

(9)f0=n(10)f1=kn+x(11)=kn+k1(12)=k(n+1)1(13)f2=k(kn+x)+x(14)=k2n+kx+x(15)=k2n+k(k1)+(k1)(16)=k2n+(k+1)(k1)(17)=k2n+k21(18)=k2(n+1)1(19)......(20)fp=kp(n+1)1

假设进行完除法得到的当前数为cur.那么如果进行了p次乘法操作,我们的可取值区间为:

[kp,kp(n+1)1]

即使kp并不是m的倍数,只要kp(n+1)1(kp)即往后kpn1个数中有m的倍数即可。

也就是说kpn1m1即可。

由上可知:乘法的最大执行次数大概也是logkm级别的。所以直接暴力即可。

所以,总时间复杂度为O(logkn×logkm)

代码:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using i128 = __int128;
typedef pair<ll,ll> pii;
#define fi first
#define se second

ll qmi(ll a,ll b)
{
	ll res = 0;
	while(b)
	{
		if(b & 1)
		{
			res = (res * a);
		}
		
	}
}

void solve()
{
	ll n,k,m,a,b;
	scanf("%lld %lld %lld %lld %lld",&n,&k,&m,&a,&b);
	if(n % m == 0)
	{
		printf("0\n");
		return ;
	}
	if(k == 1)
	{
		printf("-1\n");
		return;
	}
	ll ans = 1e18;
	i128 cur = n;
	for(int i = 0;;i++,n /= k)
	{
		if(n == 0)
		{
			ans = min(ans,i * b);
			break;
		}
		i128 l = n;
		i128 r = n;
		ll res = i * b;
		while(r / m == l / m && l % m)
		{
			l *= k;
			r = r * k + k - 1;
			res += a;
		}
		ans = min(ans,res); 
	}
	cout<<ans<<endl;
}

int main()
{
	int t = 1;
	scanf("%d",&t);
	while(t--)
	{
		solve();
	}
	return 0;
	
}
posted @   value0  阅读(53)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示