AtCoder Beginner Contest 250 DEFG题解

文章介绍了几个编程竞赛中的典型题目解决方案,包括统计1到N中满足特定条件的质数个数,使用哈希快速判断两个序列集合是否相同,计算几何问题中的披萨面积优化算法,以及股票买卖的反悔贪心策略。这些题目涉及数论、哈希表、双指针和动态规划等算法思想。
摘要由CSDN通过智能技术生成

D
题意:统计1~N中,k的个数为多少。
k要满足:k= p ∗ q 3 p*q^3 pq3,且p<q,p,q均为质数。

先把 1 0 6 10^6 106内的质数全部筛出来,然后直接暴力统计即可,注意要防止溢出。

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define x first
#define y second
const int N=2e6+10,mod=998244353;

int p[N], cnt;
bool st[N];

void get_p(int n)
{
    for (int i = 2; i <= n; i ++ )
    {
        if (st[i]) continue;
        p[++cnt] = i;
        for (int j = i + i; j <= n; j += i)
            st[j] = true;
    }
}
void work()
{
	int n;
	cin>>n;
	
	get_p(1000010);
	map<int,int> mp;
	//cout<<cnt<<endl;
	int ans=0;
	for(int i=1;i<=cnt;i++){
		for(int j=i+1;j<=cnt;j++){
			int x=p[i]*p[j]*p[j];
			if(x>n/p[j]) break;
			ans++;
		}
	}
	cout<<ans<<endl;
}
signed main()
{
	int t;
	//cin>>t;
	t=1;
	while(t--)
	{
		work();
	}
	return 0;
}

E
给定两个序列A,B,有q次询问,每次询问问A的前x个元素和B的前y个元素,其组成的两个集合是否相同,也就是数字的种类是否相同。

利用哈希,对序列进行hash,每次判断其前缀的值是否相同。注意hash的方法,若hash方法不对的话会被卡。
可以使用无符号整数来自然溢出,也可取模。

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define x first
#define y second
#define ull unsigned long long 
const int N=1e6+10;
const int mod = 1e9 + 7; 
const int P = 13331;
int a[N],b[N];
ull h1[N],h2[N];

void work()
{
	int n,q;
	cin>>n;
	set<int> s;
	
	for(int i=1;i<=n;i++){
		cin>>a[i];
		if(!s.count(a[i])){
			h1[i] = (h1[i - 1] +(a[i]+mod)*a[i]);
		}
		else h1[i]=h1[i-1];
		s.insert(a[i]);
	}
	s.clear();
	for(int i=1;i<=n;i++){
		cin>>b[i];
		if(!s.count(b[i])){
			h2[i] = (h2[i - 1] + (b[i]+mod)*b[i]);
		}
		else h2[i]=h2[i-1];
		s.insert(b[i]);
	}
	cin>>q;
	while(q--)
	{
		int x,y;
		cin>>x>>y;
		if(h1[x]==h2[y]) cout<<"Yes"<<endl;
		else cout<<"No"<<endl;
	}
}
signed main()
{
	int t;
	//cin>>t;
	t=1;
	while(t--)
	{
		work();
	}
	return 0;
}

F
题意:给定n个点,构成一张披萨,你可以选择三个点,把它们构成的面积给吃掉,若设披萨总面积为s,吃掉的面积为b,求min( ∣ 2 s − 8 b ∣ \vert{2s-8b}\vert 2s8b)。

计算几何问题,很明显可以一层循环枚举P,另一层枚举Q,然后加上P,Q,Q+1构成的面积,时间显然不允许。
正确做法是使用双指针,若其面积已经不满足条件,则减去P,Q,Q+1的面积,然后P向后移,而Q不需要重新退回。

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define x first
#define y second
typedef pair<int,int> PII;
const int N=4e5+10;
PII a[N];
PII operator-(PII a,PII b)
{
    return {b.x-a.x, b.y-a.y};
}

int chaji(PII a,PII b)
{
    return a.x*b.y-a.y*b.x;
}
int area(PII a,PII b,PII c)
{
    return abs(chaji(b-a,c-a)); //注意不能随意改变顺序
}
void work()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		int x,y;
		cin>>x>>y;
		a[i]={x,y};
	}
	int s=0;
	for(int i=3;i<=n;i++){
		s+=area(a[1],a[i-1],a[i]);
	}
	int nw=0,ans=9e18;
	for(int i=1,j=2;i<=n;i++){//双指针,很像滑动窗口 
		while(nw*4<s){
			nw+=area(a[i],a[j],a[j%n+1]);
			ans=min(ans,abs(s-4*nw));
			j=j%n+1;
		}
		nw-=area(a[j],a[i],a[i%n+1]);//注意下标 
		ans=min(ans,abs(s-4*nw)); 
	}
	cout<<ans<<endl;
}
signed main()
{
	int t;
	//cin>>t;
	t=1;
	while(t--)
	{
		work();
	}
	return 0;
}

G
题意:给定序列P,在第 i 天买进股票的话,需要 P i P_i Pi元,若要卖出股票的话,会获得 P i P_i Pi元,在任意一天,你可以做下面三种之一:
买进股票,卖出股票(如果有的话),什么都不做。
注意,当结束后若还有股票则股票作废。
问最多能获得多少钱。

做法是很经典的反悔贪心 ,就是给每个贪心的选择一个反悔的余地,从而来修正结果的正确性。具体解释见代码。

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define x first
#define y second

const int N=1e6+10;
int a[N];
 
void work()
{
	/*
	反悔贪心
	正常贪心是如果当前的价格比所持有股票最低价格高就卖 
	反悔就是,如果后来又遇到了更高的价格,就反悔,在这天卖出
	比如某几天价格为 p<q<r ,贪心就是以p的价格买入,以q的价格卖出
	反悔贪心可以再以q的价格买入,以r的价格卖出,注意实际上这样是不允许的
	只是可以看成是这样的过程,实际的操作是不以q的价格卖出,而是等到r再卖
	代码部分也用这种操作来达到反悔的目的(即在同一天卖完又买) 
	*/
	priority_queue<int,vector<int>,greater<int> > q;
	int n;
	cin>>n;
	int ans=0;
	for(int i=1;i<=n;i++){
		int x;
		cin>>x;
		if(q.size()&&q.top()<x){
			ans+=x-q.top();	//先贪心的卖出					 
			q.pop();
			q.push(x); //push进来,用于后面的反悔 
		}
		q.push(x);
	}
	cout<<ans<<endl;
}
signed main()
{
	int t;
	//cin>>t;
	t=1;
	while(t--)
	{
		work();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值