2025“钉耙编程”中国大学生算法设计春季联赛(2)

作者很菜,看不懂补药( ̄(工) ̄)俺~~~~

学历史导致的

这个就直接暴力就行了,签到题~

#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define vi vector<int>
#define sz(x) (int)x.size()
map<string ,int>ans;
string tian[]={ "jia", "yi", "bing","ding", "wu", "ji", "geng", "xin", "ren", "gui"};
string di[]={"zi","chou","yin","mao", "chen","si","wu", "wei", "shen", "you","xu", "hai"};

void solve(){
    string c;
    cin>>c;
    cout<<ans[c]<<endl;

}

signed main()
{
    int ii=0,jj=0;
    for(int i=1984;i<=2043;i++){
        string c=tian[ii]+di[jj];
        ii=(ii+1)%10;
        jj=(jj+1)%12;
        ans[c]=i;
    }
    int t;cin>>t;
    while(t--)
        solve();
    return 0;
}

学数数导致的

这个题目是要求找出数列 a1…an-1​,问有多少个正整数数对 (p,q)使得数列中存在子序列 p,0,p,q 

首先应该读懂题目,这是个正整数对,所以我们统计的时候0不能算在内,其次这是个序列

那么我们就可以先倒序遍历,统计当前位置后面出现的数字的种类个数

然后从头往后遍历,遇到0就更改idx的值,否则就进行另一个判断,看看是不是第一次出现,如果是的话,就给first_idx存储下标,这个的作用是为了防止0 1 1 这样的序列也计算进去了,其他的都挺简单啦

具体的看代码吧

#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define vi vector<int>
#define sz(x) (int)x.size()
//模拟
//记录之前的值即可
void solve(){ 
    int n;cin>>n;
    vi f1(1e6+10,0);
    vi cnt(1e6+10,0);
    vi f2(1e6+10,0);
    vi a(1e6+10,0);
    vi count(1e6+10,0);
    vi first_idx(1e6+10,1e6+10);
    int c=0;
    for(int i=0;i<n;i++)cin>>a[i];
    for(int i=n-1;i>=0;i--){
        if(f1[a[i]]==0&&a[i]!=0){
            f1[a[i]]=1;
            c++;
        }
        count[i]=c;
    }
    int idx=100001001;
    int ans=0;
    for(int i=0;i<n;i++){
        if(a[i]!=0){
            if(first_idx[a[i]]==1e6+10)first_idx[a[i]]=i;
            if(cnt[a[i]]==0||f2[a[i]]==1){//没出现过或者已经算过了
                cnt[a[i]]++;
            }else if(first_idx[a[i]]<idx){//之前已经出现过了并且最先出现的小于idx
                if(idx<i){//可以计算了
                    ans+=count[i+1];
                    f2[a[i]]=1;
                }
            }
        }else{
            idx=i;
        }
    }
    cout<<ans<<endl;
}

signed main()
{
    ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
    int t;cin>>t;
    while(t--)
    solve();
    return 0;
}

学 DP 导致的

要求找最长子序列,直接打板子就好啦~

但是k需要处理一下,最长就是拼接26次

#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define vi vector<int>
#define sz(x) (int)x.size()
//最长子序列模板啦

void solve(){   
    
    int  q[100010]={0};
    string a;cin>>a;;
    string k;cin>>k;
    int t = (k.length() >= 3) ? 26 : min(26, stoi(k));
    string b=a;
    for(int i=0;i<t-1;i++)a=a+b;
    int n=sz(a);
    q[0] = -2e9;
    int len = 0;
    for (int i = 0; i < n; i ++)
    {
        int l = 0, r = len;
        while (l < r)
        {
            int mid = (l + r + 1) >> 1;
            if (q[mid] < a[i]) l = mid;
            else r = mid - 1;
        }
        len = max(len, r + 1);
        q[r + 1] = a[i];
    }
    cout<<len<<endl;
}

signed main()
{
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int t;cin>>t;
    while(t--)
    solve();
    return 0;
}

学几何导致的

这个是个规律,我们每次实现必须O(1)来实现

观察可以发现,如果k为奇数,那么一定没有结果啦

其次我们对于0来说,第一个垂直的是0+k/2 ,第二个是0+k/2+k,第三个是0+k/2+k+k.....

那么到这里应该就发现规律了,一直加到n即可,那么第二个就是从1开始了,第一个垂直的是1+k/2 ,第二个是1+k/2+k,第三个是1+k/2+k+k.....

在这我们先使k/=2;

我们可以得到0开始的垂直的数量为t=(n-1-k)/(k*2)+1;

那么是不是最后还会剩几个,是多少呢?其实余数就是mod=(n-1-k)%(k*2);

那么数量为t的计算完了,依次是t-1,t-2......1 ,而对于每个,他的数量都是2*k

直接求和相乘就行了ans=t*(mod+1)+((1+t-1)*(t-1)/2)*(2*k);

看代码吧

#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define vi vector<int>
#define sz(x) (int)x.size()

void solve(){   
    int n,k;cin>>n>>k;
    if(k%2==1||(2*n<=k)){//差个条件判断
        cout<<0<<'\n';
        return;   
    }
    k/=2;
    int ans=0;
    int t=(n-1-k)/(2*k)+1;
    int mod=(n-1-k)%(2*k);
    ans=t*(mod+1)+((1+t-1)*(t-1)/2)*(2*k);
    cout<<ans<<'\n';

}

signed main()
{
    ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
    int t;cin>>t;
    while(t--)
    solve();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值