2024年寒假开学赛题解
题目 | 知识点 | 难度/10 |
---|---|---|
组合数(易) | 签到题 | 1 |
最高海拔 | 签到题/前缀和 | 2 |
连续自然数 | 数学/前缀和 | 3 |
这题主要考察了找规律 | 递推 | 4 |
这题主要考察了画画 | 贪心 思维 | 4 |
组合数(难) | 数学 + 预处理 | 5 |
这题主要考察了图的遍历 | DFS&BFS | 6 |
昨夕是何天 | 模拟 | 6 |
修路 | 并查集 | 6 |
倍数 | 倍数的特点 | 6 |
算两次 | 质因数分解 | 7 |
或与异或 | 位运算 思维 | 8 |
这题主要考察了DP | DP | 8 |
combinatorial number(easy)
本场第一签到,根据公式直接算就行了,注意开long long
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100010, mod = 1e9 + 7;
void solve()
{
int a, b;
cin >> a >> b;
LL ans1 = 1, ans2 = 1;
for (int i = 1, j = a; i <= b; i ++, j --)
{
ans1 *= j;
ans2 *= i;
}
cout << ans1/ ans2 << endl;
}
int main()
{
int q;
cin >> q;
while (q --) solve();
return 0;
}
combinatorial number(normal)
由于 ( a + b ) % m o d = ( a % m o d + b % m o d ) % m o d (a+b)\%mod = (a\%mod + b \% mod)\%mod (a+b)%mod=(a%mod+b%mod)%mod,根据递推公式 C n m = C n − 1 m − 1 + C n − 1 m C^m_n=C^{m-1}_{n-1}+C_{n-1}^m Cnm=Cn−1m−1+Cn−1m,可以求出所有的答案,每次询问时输出预处理出来的答案即可
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 2010, mod = 1e9 + 7;
int c[N][N];
void init()
{
for (int i = 0; i < N; i ++ )
for (int j = 0; j <= i; j ++ )
if (!j) c[i][j] = 1;
else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
}
int main()
{
init();
int n;
cin >> n;
while (n --)
{
int a, b;
cin >> a >> b;
cout << c[a][b] << endl;
}
return 0;
}
大家去学一下逆元,也可以用下面的方法做
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define int long long
const int MAX=1e5+10;
const ll mod=1e9+7;
int n, m;
int fact[MAX], infact[MAX];
int quick_power(int a,int b)
{
int ans = 1;
while(b)
{
if(b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
void init()
{
fact[0] = infact[0] = 1;
for(int i = 1 ; i < MAX ; i ++)
{
fact[i] = fact[i-1] * i % mod;
infact[i] = infact[i-1] * quick_power(i,mod-2) % mod;
}
}
void solve()
{
cin >> n >> m;
cout << (fact[n]%mod*infact[m]%mod*infact[n-m]%mod)%mod << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int _;
cin >> _;
//_ = 1;
init();
while(_--) solve();
return 0;
}
这题主要考察了画画
这种题可以多观察一下样例
可以贪心地先把一行填满,每填一个方块可贡献2条需要的对角线(不重复的),剩下的只能填一个,贡献一条对角线了
void solve()
{
int n,m;
cin >> n >> m;
n = n + max(0,n-2);
if(m <= 2*n){
if(m&1) cout << (m >> 1) + 1 << endl;
else cout << (m >> 1) << endl;
}else {
cout << n + (m - 2 * n) << endl;
}
}
这题主要考察了DP
#include<bits/stdc++.h>
using namespace std;
int n, m;
const int N = 510;
double dp[N][N];
void solve()
{
cin >> n >> m;
//dp[i][j]表示前i个人用了j个仙贝的好感度,k为枚举第i个人获得的仙贝数
for(int i = 1 ; i <= n ; i++)
for(int j = 1 ; j <= m ; j++)
for(int k = 1 ; k <= j ; k++)
dp[i][j] = max(dp[i][j],dp[i-1][j-k] + k*1.0/(m*1.0-(j-k)*1.0));
}
int main()
{
int _;
_ = 1;
while(_--) solve();
printf("%.6f",dp[n][m]);
}
这题主要考察了图的遍历
跑一边dfs和bfs的板子就行了,但要注意题目要求,如果有很个节点可以遍历,先遍历序号小的。
所有用set存边或者用vector存,然后排序
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 100;
set<int> e[N];
int n, m;
int vis[N];
queue<int> q;
void dfs(int u)
{
if(vis[u]) return;
vis[u] = 1 ;
cout << u << " ";
for(auto v : e[u]) dfs(v);
}
void bfs()
{
q.push(1);
while(q.size())
{
int u = q.front(); q.pop();
if(vis[u]) continue;
vis[u] = 1;
cout << u << " ";
for(int v : e[u]) q.push(v);
}
}
int main()
{
cin >> n >> m;
for(int i = 1 ; i <= m ; i ++)
{
int u, v;
cin >> u >> v ;
e[u].insert(v);
}
dfs(1);
cout << endl;
memset(vis , 0 , sizeof vis);
bfs();
return 0;
}
这题主要考察了找规律
题目肯定是骗人的,这哪儿来的规律,和爬楼梯类似,第i阶只能由第i-1和i-阶爬到,直接递推就好了,注意开long long
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
long long f[N];
int a[N];
void solve(){
//这里放自己程序的主函数
//自己程序的其他函数,直接写在其他地方就行了
memset(f,0,sizeof f);
int n;
scanf("%d",&n);
for(int i = 0 ; i < n ; i ++) scanf("%d",&a[i]);
f[0] = 0 , f[1] = a[0];
for(int i = 2 ; i <= n ; i ++)
f[i] = min(f[i-1] + (long long)a[i-1],f[i-2]+(long long)a[i-2]);
printf("%lld",f[n]);
}
int main(void)
{
solve();
return 0;
}
我就站在你眼前,你看我有几分像从前
这题是第三次考核题目,但出题的时候发现还是没有一个人补体,所有出出来让大家体会一下没补题的后悔
正解是质因数分解,但是我们给的程序直接取合适的模数也可以
#include <bits/stdc++.h>
#define ll long long
#define N 100005
int f[N],p[N],nu1[N],nu2[N];
int num1=0,num2=0;
int ma=0;
using namespace std;
int num=0;
void init_p()
{
f[1]=1;
num=0;
for(int i=2;i<=N;i++){
if(f[N]==0){
p[++num]=i;
}
for(int j=1;j<=num;j++){
if(i*p[j]<N) f[i*p[j]]=1;
if(i%p[j]==0||i*p[j]>N) break;
}
}
}
int main()
{
init_p();
int m;
scanf("%d",&m);
for(int i=1;i<=m;i++){
int op,x;
scanf("%d%d",&op,&x);
if(x<0){
num1++;
x=-1*x;
}
ma=max(ma,x);
if(op==1){
for(int i=1;p[i]<=x;i++){
if(f[x]==0){
nu1[x]++;
break;
}
while(1){
if(x%p[i]==0){
nu1[p[i]]++;
x/=p[i];
}
else break;
}
}
}
else {
for(int i=1;p[i]<=x;i++){
if(f[x]==0){
nu1[x]--;
break;
}
while(1){
if(x%p[i]==0){
nu1[p[i]]--;
x/=p[i];
}
else break;
}
}
}
}
scanf("%d",&m);
for(int i=1;i<=m;i++){
int op,x;
scanf("%d%d",&op,&x);
if(x<0){
num2++;
x=-1*x;
}
ma=max(ma,x);
if(op==1){
for(int i=1;p[i]<=x;i++){
if(f[x]==0){
nu2[x]++;
break;
}
while(1){
if(x%p[i]==0){
nu2[p[i]]++;
x/=p[i];
}
else break;
}
}
}
else {
for(int i=1;p[i]<=x;i++){
if(f[x]==0){
nu2[x]--;
break;
}
while(1){
if(x%p[i]==0){
nu2[p[i]]--;
x/=p[i];
}
else break;
}
}
}
}
if(num1%2!=num2%2){
printf("NO");
return 0;
}
for(int i=1;p[i]<=ma;i++){
if(nu1[p[i]]!=nu2[p[i]]){
printf("NO");
return 0;
}
}
printf("YES");
return 0;
}
修路
考察并查集 如果两个村庄要修路,就合并,每次合并之前查询
#include <bits/stdc++.h>
#define ll long long
#define N 1000005
using namespace std;
int p[N],r[N];
int find(int x)
{
if(x==p[x]) return p[x];
else {
p[x]=find(p[x]);
return p[x];
}
}
void join(int x,int y)
{
int rx=find(x),ry=find(y);
if(r[rx]<r[ry]) p[rx]=ry;
else {
p[ry]=rx;
if(r[rx]==r[ry]) r[rx]++;
}
}
void init(int n)
{
for(int i=1;i<=n;i++){
p[i]=i;
r[i]=1;
}
}
int n,m;
void slove()
{
scanf("%d%d",&n,&m);
init(n);
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
if(find(x)==find(y)){
printf("No\n");
}
else {
join(x,y);
printf("Yes\n");
}
}
}
int main()
{
ll t;
t=1;
while(t--){
slove();
}
return 0;
}
倍数
大家要去了解一下怎么判断这些数的倍数
#include <bits/stdc++.h>
#define ll long long
using namespace std;
string s;
int len;
int num[10];
ll get_sum()
{
ll ret=0;
for(int i=0;i<len;i++){
ret+=(s[i]-'0');
}
return ret;
}
ll get_he(int x)
{
ll ret=0;
for(int i=x;i>=1;i--){
ret=ret*10+(s[len-i]-'0');
}
return ret;
}
int main()
{
int q;
scanf("%d",&q);
while(q--){
cin>>s;
len=s.length();
ll sum=get_sum();
if((s[len-1]-'0')%2==0) {
num[2]++;
if(sum%3==0) num[6]++;
}
if(sum%3==0) num[3]++;
if(len>=2){
int x=get_he(2);
if(x%4==0) num[4]++;
}
else {
int x=get_he(1);
if(x%4==0) num[4]++;
}
if(s[len-1]=='0'||s[len-1]=='5'){
if(s!="0") num[5]++;
}
if(len>=3){
int x=get_he(3);
if(x%8==0) num[8]++;
}
else if(len==2){
int x=get_he(2);
if(x%8==0) num[8]++;
}
else {
int x=get_he(1);
if(x%8==0) num[8]++;
}
if(sum%9==0) num[9]++;
}
for(int i=2;i<=9;i++){
if(i==7) continue;
printf("%d ",num[i]);
}
}
或与异或
#include <bitsdc++.h>
#define ll long long
using namespace std;
int q;
ll x,y,z;
ll a,b;
int get_ans()
{
ll p=1;
do{
ll xx=x%2,yy=y%2,zz=z%2;
x/=2;y/=2;z/=2;
if(xx==0&&yy==1) return 0;
if(zz==0){
if(xx!=yy) return 0;
else {
if(xx==1){
a+=p;
b+=p;
}
}
}
else {
if(xx==0||yy==1) return 0;
else {
a+=p;
}
}
p*=2;
}while(x||y||z);
return 1;
}
int main()
{
scanf("%d",&q);
while(q--){
scanf("%lld %lld %lld",&x,&y,&z);
a=0,b=0;
if(get_ans()){
printf("%lld %lld\n",a,b);
}
else printf("-1 -1\n");
}
return 0;
}
#include <bitsdc++.h>
#define ll long long
using namespace std;
int q;
ll x,y,z;
ll a,b;
int get_ans()
{
ll p=1;
do{
ll xx=x%2,yy=y%2,zz=z%2;
x/=2;y/=2;z/=2;
if(zz==0){
if(xx!=yy) return 0;
else {
if(xx==1){
a+=p;
b+=p;
}
}
}
else {
if(xx==0||yy==1) return 0;
else {
a+=p;
}
}
p*=2;
}while(x||y||z);
return 1;
}
int main()
{
scanf("%d",&q);
while(q--){
scanf("%lld %lld %lld",&x,&y,&z);
if((x^y)==z&&(x|y)==x&&(x&y)==y) printf("%lld %lld\n",x,y);
else printf("-1 -1\n");
}
return 0;
}
前夕是何天
按照题意,计算日期即可
这里有一份把日历存下来,每次询问直接访问的代码
#include <bits/stdc++.h>
using namespace std;
string data[400005];
map <string,int> mp;
int num=0;
string int_to_str(int x,int len)
{
string ret="";
for(int i=1;i<=len;i++){
ret=(char)(x%10+'0')+ret;
x/=10;
}
return ret;
}
void get_data()
{
int day[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
for(int y=2000;y<=3000;y++){
if((y%4==0&&y%100!=0)||(y%400==0)) day[2]=29;
else day[2]=28;
string year=int_to_str(y,4)+'-';
for(int m=1;m<=12;m++){
string month=int_to_str(m,2)+'-';
for(int d=1;d<=day[m];d++){
string time=year+month+int_to_str(d,2);
data[++num]=time;
mp[time]=num;
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
get_data();
int q;
string t;
int k;
cin>>q;
while(q--){
cin>>t>>k;
int id=mp[t];
cout<<data[id-k]<<'\n';
}
return 0;
}
最高海拔
应该算是本场第二签,前缀和求出来然后找到最大值,输出这个点就好了
#include <stdio.h>
#define ll long long
int main()
{
ll ma=0,ip=0,sum=0;
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
sum+=x;
if(sum>ma){
ma=sum;
ip=i;
}
}
printf("%d %d",ip,ma);
return 0;
}