2024郑州邀请赛暨河南省赛题解

知识点模块

1.当一个数是10的倍数(记作n),我们要把这个数变成另一个数(记作k)的倍数,我们该怎么做呢,我们只需要n+k-(n+k)%k即可,比如100要变成23的倍数,只需要100+23-(100+23)%23=115即可
ans=n+k-(n+k)%k
2.multiset如果有多个重复的数字,我们要删掉其中一个,只能通过地址来删除,下题的应用就是这样
se.erase(se.lower_bound(mark))
3.快速幂的模板

点击查看代码
ll ksm(ll a,ll n)//a为底数,n为次方
{
    ll res=1;
    while(n>0){
       if(n&1) res=res*a*%mod;
       a=a*a%mod;
       n=n>>1;
    }
}

4.正式地说,答案对 998 244 353 取模表达了如下含义。令 M = 998 244 353,可以证明答案可表示为既约分数 p/q,其中 p 和 q 均为整数,且 q ̸≡ 0 (mod M)。你需要输出 p · q^−1 mod M。换句话说,你需要输出满足 0 ≤ x < M 且 x · q ≡ p (mod M) 的整数 x。

点击查看代码
//怎么翻译这句话呢?其实可以这么记忆
//题目是这么要求的:由于答案可能不是整数,为了方便计算,你只需要求出这个值对 998 244 353 取模的结果。
//也就是说计算概率肯定是分数,那么假如结果是1/2*2/3*1/3
//我们就以表示成ans=ans*cnt[x]%mod*ksm(q,mod-2)%mod; ans%=mod;
//cnt就是分子,q为分母,1/x表示为ksm(x,mod-2)原理是费马小定理
记好这个模板 ans=ans*cnt[x]%mod*ksm(q,mod-2)%mod; ans%=mod;

5.next_permutation(s.begin(),s.end())的使用
会按照当前数组的顺序接下去全排列输出,排完了就返回0
6.埃筛法模板

点击查看代码
bool isp[100005];
void isf()
{
for(int i=0;i<100005;i++) isp[i]=1;
	isp[0]=isp[1]=0;
	for(int i=2;i<100002;i++)
	{
		if(isp[i])
		{
			for(int j=2*i;j<=100001;j+=i)
			{
				isp[j]=0;
			}
		}
	}
}

题目以pdf的形式展现
https://codeforces.com/group/L9GOcnr1dm/contest/524066/attachments/download/25503/problemset_codeforces.pdf

Problem A. Once In My Life
这一题我们就用到了上述的知识点1,我们需要把让d出现两次,那么构造的方法就是123456789d0000,后面几个0取决于n有几位数,那么在把这个123456789d000按照知识点1来处理,最后除以n即可

点击查看代码
#include <bits/stdc++.h>
#define int long long
#define mod 998244353
#define ll long long
#define endl '\n'
using namespace std;

void solve()
{
	string n;
	int d,ans=123456789;
	cin>>n>>d;
	int len=n.size();
	ans=(ans*10+d)*pow(10,len);
	int k=stoi(n);
	ans=(ans+k)-(ans+k)%k;
	cout<<ans/k<<endl;
	
}

signed main()
{
  	ios::sync_with_stdio(false), cin.tie(0);
	  int t=1;
  	cin>>t;
  	while(t--) solve();

  	return 0;
}

Problem B. 扫雷 1

这题本质上是每个区间的贪心问题,我们从末尾开始预处理,给每段区间的值都改为最小值,然后按顺序遍历即可,自己写几个样例看看,学习预处理的思路会很有帮助

点击查看代码
#include <bits/stdc++.h>
#define int long long
#define mod 998244353
#define ll long long
#define endl '\n'
using namespace std;

void solve()
{
	int n;
	cin>>n;
	vector<int>a(n+1),b(n+1);
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	}	
	
	int minn=1e9;
	for(int i=n;i>=1;i--)
	{	
		if(a[i]<minn){
			minn=a[i];
		}
		a[i]=minn;
	}
	
	//for(auto t:a) cout<<t<<" ";
	int cnt=0,ans=0;
	for(int i=1;i<=n;i++)
	{
		cnt++;
		if(cnt>=a[i])
		{
			ans+=cnt/a[i];
			cnt%=a[i];
		}
	}
	cout<<ans;
}

signed main()
{
  	ios::sync_with_stdio(false), cin.tie(0);
	  int t=1;
  	//cin>>t;
  	while(t--) solve();

  	return 0;
}

Problem F. 优秀字符串
签到题,按题意模拟即可

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
#define int long long
typedef pair<int,int> pii;
int ans=0;
void solve()
{   
    string s;
    map<char,int>mp;
    cin>>s;
    if(s.size()!=5) return ;
    if(s[2]!=s[4]) return;
    for(int i=0;i<=3;i++)
    {
        mp[s[i]]++;
        if(mp[s[i]]>1) return ;
    }
    
    ans++;
}

signed main () {
    int t=1;
    cin>>t;
    while(t--) solve();
    cout<<ans;
}

Problem H. 随机栈
这题有两个需要处理的点
1.最后递增的序列的概率是多少呢,我们可以这样操作,先把所有的数字都放入multiset里面,然后再放一个1e10,可以开一个变量k=-1(就是一个参照值,每次用来找比已经取出的最小值的数),每一次从这个取出最小值,怎么实现呢,使用lower_bound,然后找到大于等于第一个minn的数,取完以后记得把这个数从堆里面删掉,用知识点2,然后再更新k重复这个过程即可
2.就是对这个结果分数的处理,按照题目的要求,我们得到的概率是分数,但是要我们输出的却是一个整数,那么就使用知识点4来处理

点击查看代码
#include <bits/stdc++.h>
#define int long long
#define mod 998244353
#define endl '\n'
using namespace std;
typedef pair<int,int> pii;
int ksm(int a,int n)//a为底数,n为次方 
{
	int res=1;
	while(n>0)
	{
		if(n&1)res=res*a%mod;
		a=a*a%mod;
		n=n>>1;
	}
	
	return res;
}


void solve()
{
	int n;
	cin>>n;
	multiset<int>se;
	se.insert(1e10);
	map<int,int>cnt;
	int k=-1,ans=1;
	//k其实就是一个更新的参照,用来找这个堆里的最小值,然后不断的实现取出来的操作 
	for(int i=0;i<2*n;i++)
	{
		int x;
		cin>>x;
		if(x!=-1)
		{
			se.insert(x);
			cnt[x]++;
		}
		
		else{
			int mark=*se.lower_bound(k);
			if(mark==1e10)//也就是插入的元素没有匹配的 
			{
				ans=0;
			}
			else{
			int q=se.size()-1;
			ans=ans*cnt[mark]%mod*ksm(q,mod-2)%mod;
			ans%=mod;
			cnt[mark]--;
			if(cnt[mark]==0) cnt.erase(mark);
			se.erase(se.lower_bound(mark));//这里因为set里面会有重复的,只有删地址才能实现只删一个
			k=max(k,mark);
			}
		}
		
	}
	cout<<ans<<endl;

}

signed main()
{
  	ios::sync_with_stdio(false), cin.tie(0);
	  int t=1;
  	//cin>>t;
  	while(t--) solve();

  	return 0;
}

Problem J. 排列与合数
1.首先还是明确什么是合数,除了1以外的数不是质数就是合数,我们使用唉筛法,先标记一下一下用来快速的判断
2.然后我们先对这个数进行排序,sort一下,然后再用next_permutation函数,符合题意输出终止即可

点击查看代码
#include <bits/stdc++.h>
#define int long long
#define mod 100000007;
using namespace std;
int a[300005],b[300005];
int n;
bool isp[100005];
void isf()
{
	isp[0]=isp[1]=0;
	for(int i=2;i<100002;i++)
	{
		if(isp[i])
		{
			for(int j=2*i;j<=100001;j+=i)
			{
				isp[j]=0;
			}
		}
	}
}


void solve()
{
	int n;

	string s;
	cin>>s;
	sort(s.begin(),s.end());
	
	do{
		if(s[0]=='0') continue;
		int mark=0;
		for(auto t:s) mark=10*mark+t-'0';
		if(!isp[mark]){
			cout<<s<<endl;
			return ;	
		}
	}while(next_permutation(s.begin(),s.end()));
	cout<<"-1"<<endl;

	
}
signed main()
{
  	int t=1;
  	memset(isp,1,sizeof isp);
  	isf();
  	cin>>t;
  	while(t--) solve();

  	return 0;
}

Problem M. 有效算法
1.拆一下绝对值会发现 ai-k×bi< x <ai+k×bi,而x是符合每次a的变化的,那么就代表每个对应的ai和bi都会对应一个区间,而这些区间都会有交集,而且这里一定要注意区间的缩小,否则会出现一个大区间里面包含很多个不交叉的小区间的情况
2.明确了上述这个点以后,只要二分枚举一下k然后验证即可,注意k=0是合法的

点击查看代码
#include <bits/stdc++.h>
#define int long long
#define mod 100000007;
using namespace std;
int a[300005],b[300005];
int n;
bool check(int x)
{
	int ll=a[1]-x*b[1],rr=a[1]+x*b[1];	
	for(int i=2;i<=n;i++)
	{
		int ls=a[i]-x*b[i],rs=a[i]+x*b[i];
		if(ls>ll) ll=ls;
		if(rs<rr) rr=rs;
		if(ll>rr) return 0;
	}
	return 1;
}

void solve()
{

	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++) cin>>b[i];
	
	int l=0,r=1e10;
	while(l<r)
	{
		int mid=(l+r)>>1;
		if(check(mid)) r=mid;
		else l=mid+1;
		
	}
	cout<<l<<endl;
	
}
signed main()
{
  	int t=1;
  	cin>>t;
  	while(t--) solve();

  	return 0;
}

Problem D. 距离之比
https://blog.csdn.net/qq_45809243/article/details/138890493 理解来自于这里
1.首先我们设PQ=X,∣PM∣=x∣cosθ∣,∣QM∣=x∣sinθ∣。对曼哈顿距离与欧式距离的比值化简以后就是∣sinθ∣+∣cosθ∣,原式化简可以发现在0-Π/2和Π/2-Π之间,只要越靠近45度,这个比值越大
2.我们在一个坐标轴的一个象限内,取A,B,C三个点,按照X的大小(距离y轴的远近来排序),可以发现,这三条线中斜率最大的一定是来自于AB或者BC,即,斜率最大的不会出现在距离y轴的距离不相邻的点,为什么我一定强调这是距离y轴的距离而非x呢?后面会醒悟的

3.假设我们要找一条斜率更接近90度的直线,那么我们一定是在点2中这样相邻的点取遍历寻找的,那么我们要找一条靠近45度的直线,是不是也是这个道理呢?答案是同理的,如果我们按照距离y=x的远近程度从小到大排序,然后再遍历找最值,那么这就是答案了
4.一个点逆时针旋转45度,便得到了与我们要找的90度的直线等价的点,根据旋转矩阵的知识点,x转完以后左边便成为x+y(这个知识点见 https://zhuanlan.zhihu.com/p/183973440?utm_id=0 ),而135度的同理,只是转完以后变成了x-y
5.那么我们便可以按照两种情况去排序点,然后找到比值的最值即可

点击查看代码
#include <bits/stdc++.h>
#define int long long
#define mod 998244353
#define endl '\n'
using namespace std;
typedef pair<int,int> pii;
bool cmp1(pii a,pii b)
{
	return a.first+a.second<b.first+b.second;
}

bool cmp2(pii a,pii b)
{
	return a.first-a.second<b.first-b.second;
}

double cal(pii a,pii b)
{
	int x1=a.first,y1=a.second;
	int x2=b.first,y2=b.second;
	double res=( ( abs(x1-x2)+abs(y1-y2) )/sqrt( pow(x1-x2,2)+pow(y1-y2,2) ));
	return res;
} 


void solve()
{
	int n;
	cin>>n;
	vector<pii>p(n) ;
	for(int i=0;i<n;i++) cin>>p[i].first>>p[i].second;
	sort(p.begin(),p.end(),cmp1);
	//for(int i=0;i<n;i++) cout<<p[i].first<<" "<<p[i].second<<endl;
	
	double maxx=-1;
	for(int i=0;i<n-1;i++)
	{
	
		maxx=max(maxx,cal(p[i],p[i+1]))	;
	}
	sort(p.begin(),p.end(),cmp2);
	for(int i=0;i<n-1;i++)
	{
	
		maxx=max(maxx,cal(p[i],p[i+1]))	;
	}
		cout<<fixed<<setprecision(12)<<maxx<<endl;
}

signed main()
{
  	ios::sync_with_stdio(false), cin.tie(0);
	int t=1;
  	cin>>t;
  	while(t--) solve();

  	return 0;
}

posted on 2024-05-18 01:02  swj2529411658  阅读(51)  评论(0编辑  收藏  举报

导航