2025 SUM-ACM 第二周周报

(年前年后的一块一起写了)(一些比较有意思的题在这里写一下)

Problem - B - Codeforces

题意

将一个长n的数组按顺序分成k个不为空的子数组,将所有第偶数个子数组合成一个数组并且在末尾加一个0,第一个数序号为1,定义此数组的代价为最小的 满足序号为i的数不等于i 的 i

输出最小的代价

思路

用贪心,希望第二的子数组的第一个数不是1,此时的代价就为1,为最小值

那如果前面有数为1,那么就划给第一个数组,注意第一个数组的最大长度为n-k+1

但实际上要求第二的数组的第一个数不为1就行,所以遍历前 n-k+1+1 个数来判断

如果前 n-k+1+1 个数都为1,此时分两种情况

第一种: n==k 此时每个数为一个子数组,取偶数位的数组成新数组按题意遍历找到满足要求的数就行

第二种: n>k 此时至少前三个数为1 ,将第一个1给第一个子数组,第二、第三个1 给第二个子数组,此时偶数数组的第二个数为1(不等于2)满足题意,代价为2,为当前情况下的最小值

(别走,看吐槽)

吐槽

注意特判前n-k+1+1个数中非1 的数是否只有一个并且是第一个数,如果是这种情况,按前 n-k+1+1 个数都为1 来处理,因为第一个子数组也不能为空

AC代码

#include<bits/stdc++.h>
using namespace std;

#define int long long

int n,k;
int arr[200005];
int cha;

void solve(){
    int ans = 1e9+9;
   cin>>n>>k;
   cha = n-k+1;
   for(int i=1;i<=n;++i) cin>>arr[i];
   for(int i=n+1;i<=n+2;++i) arr[i]=0;
    for(int i=1;i<=cha+1;++i){
        if(arr[i]!=1 && i!=1){
            ans = min(ans,(long long)1);
            break;
        }
    }
    if(ans != 1){
        if(cha+1>=3){
            ans = min(ans,(long long)2);
        }
    }
    if(ans > 2){
        for(int i=2;i<=n+2;i+=2){
            if(arr[i]!=(i>>1)){
                ans = min(ans,(i>>1));
            }
        }
    }
    cout<<ans<<'\n';
}

signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int _=1;
    cin>>_;
    while(_--){
        solve();
    }
    return 0;
}

PTA - 千手观音

题意

给出一些按升序排列的以字符串表示单个数字的数,求每个字符串(数)间的大小关系,无明确关系的按字典序顺序输出

思路

很明显是一个拓扑排序,同一层中的用一个优先队列来存就能实现按字典序输出

但拓扑排序的图怎么建出来呢?

这里我们明确一个大前提:

我们只能明确 两个位数相同且已知大小关系的的数中 从高位到低位 第一个不相同的数的大小关系

例如:

若 aa.ab.cc>aa.ac.bb 我们只能明确ab>ac

在这个大前提下对输入建图,我们就能进行拓扑排序得到答案了

吐槽

优先队列记得开成小根

AC代码

#include<bits/stdc++.h>
using namespace std;

//#define int long long

int n;
string s;
vector<string> now,before;

unordered_map<string,vector<string> > adjacency_list;
priority_queue<string,vector<string>,greater<string> > pq;
unordered_map<string,int> indegree;

queue<string> ans;

vector<string> cut(string s){
    vector<string> res;
    string ss;
    for(int i=0;i<s.size();++i){
        if(s[i]!='.') ss+=s[i];
        else{
            res.push_back(ss);
            if(!indegree.count(ss)) indegree[ss]=0;
            ss.clear();
        }
    }
    if(!ss.empty()){
        res.push_back(ss);
        if(!indegree.count(ss)) indegree[ss]=0;
        ss.clear();
    }
    return res;
}


void solve(){
    cin>>n;
    cin>>s;
    before=cut(s);
    for(int i=1;i<n;++i){ //cout<<i;
        cin>>s;
        now = cut(s);
        if(before.size() == now.size()){
            for(int j=0;j<now.size();++j){
                if(before[j] != now[j]){
                    adjacency_list[before[j]].push_back(now[j]);
                    ++indegree[now[j]];
                    break;
                }
            }
        }
        before = now;
    }//邻接表建图↑
    //拓扑排序↓
    for(auto xx:indegree){
        if(xx.second==0) pq.push(xx.first);
    } //cout<<pq.size();
    while(!pq.empty()){
        s = pq.top();
        pq.pop();
        ans.push(s);
        for(auto xx:adjacency_list[s]){
            --indegree[xx];
            if(indegree[xx]==0) pq.push(xx);
        }
    } //cout<<ans.size();
    while(!ans.empty()){
        if(ans.size()==1){
            cout<<ans.front();
            return;
        }
        cout<<ans.front()<<".";
        ans.pop();
    }
}

signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int _=1;
    //cin>>_;
    while(_--){
        solve();
    }
    return 0;
}

2025牛客寒假算法基础集训营4

这一场的题都还有点意思,多写几个

B-Tokitsukaze and Balance String (easy)_2025牛客寒假算法基础集训营4

思路

因为n很小,所以直接DFS就好了,不过是把字符串传进DFS里再导出,写起来挺有意思的,也很快

对导出的所有字符串进行以下操作:

遍历每一个字符,反转之后进行check(),若可以,则++res

AC代码

#include<bits/stdc++.h>
using namespace std;

#define int long long

int n;
string s;
vector<string> v;

void dfs(string em,string s,int i){
    if(em.size()==n){
        v.push_back(em);
        return ;
    }
    if(i>=n) return ;
    if(s[i]!='?'){
        dfs(em+s[i],s,i+1);
    }
    else{
        dfs(em+'1',s,i+1);
        dfs(em+'0',s,i+1);
    }
}

bool check(string s){
    int cnt0=0,cnt1=0;
    for(int i=0;i<n-1;++i){
        if(s[i]=='0' && s[i+1]=='1') ++cnt0;
        if(s[i]=='1' && s[i+1]=='0') ++cnt1;
    }
    if(cnt0==cnt1) return true;
    else return false;
}

void solve(){
    v.clear();
    cin>>n;
    cin>>s;
    dfs("",s,0);
    int res = 0;
    for(auto ss:v){ //cout<<ss<<'\n';
        for(int i=0;i<n;++i){
            s = ss;
            if(s[i]=='0') s[i]='1';
            else s[i]='0';
            //cout<<s<<" ";
            if(check(s)) ++res;
        }
        //cout<<ss<<" "<<res<<'\n';
    }
    cout<<res%(1000000007)<<'\n';
}

signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int _=1;
    cin>>_;
    while(_--){
        solve();
    }
    return 0;
}

D-Tokitsukaze and Concatenate‌ Palindrome_2025牛客寒假算法基础集训营4

思路

因为a,b串都能重新随意排列,所以我们不用实际的将其排列出来,用哈希map储存两个串中各个字符的个数即可

接下来分情况,

首先是最特殊的情况,a,b串的长度相等,则拼接串的中心即使a,b串的分界点,最好处理,看a中有多少个b没有(或不够)的字符,即为答案

接下来是a串比b串长的情况,首先统计a中有多少b中的字符,这些就是a与b对称的位置上要放的字符(对称的就从a的map中删去),在此之外统计a中还剩的字符有多少能凑出对称的(即map[char]/2),但是要注意,这个在a的长度内对称的长度不能超过(a.size()-b.size() ),因为要留位置与b中的字符对称,统计的字符数与长度取较小值就好了

b串比a串长的情况同理

AC代码

#include<bits/stdc++.h>
using namespace std;

#define int long long

int n,m;
string a,b;

unordered_map<char,int> ah,bh;

void solve(){
   cin>>n>>m;
   cin>>a;
   cin>>b;
   int sum = n+m;
   ah.clear(); bh.clear();
   for(int i=0;i<n;++i){
    ++ah[a[i]];
   }
    for(int i=0;i<m;++i){
        ++bh[b[i]];
    }

    if(n==m){
        int cnt = 0;
        for(int i=0;i<m;++i){
            if(ah[b[i]]) --ah[b[i]],cnt+=2;
        }
        cout<<(sum-cnt)/2<<'\n';
    }
    else if(n<m){
        int cnt = 0;
        for(int i=0;i<n;++i){
            if(bh[a[i]]) --bh[a[i]],cnt+=2;
        }
        int bi = m-n; int ec = 0;
        for(auto xx:bh){
            if(xx.second>=2){
                ec += xx.second-xx.second%2;
            }
        }
        ec = min(ec,bi);
        cout<<(sum-cnt-ec)/2<<'\n';
    }
    else{
         int cnt = 0;
        for(int i=0;i<m;++i){
            if(ah[b[i]]) --ah[b[i]],cnt+=2;
        }
        int bi = n-m; int ec = 0;
        for(auto xx:ah){
            if(xx.second>=2){
                ec += xx.second-xx.second%2;
            }
        }
        ec = min(ec,bi);
        cout<<(sum-cnt-ec)/2<<'\n';
    }
}

signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int _=1;
    cin>>_;
    while(_--){
        solve();
    }
    return 0;
}

E-Tokitsukaze and Dragon's Breath_2025牛客寒假算法基础集训营4

思路

虽然数据范围给的好像能暴力,但是会TLE(没错,我试了(悲

这种按顺序求和的很容易想到前缀和优化,这里变化一下,用斜缀和

因为是X型的求和范围,所以开两个数组,分别存主对角线方向的斜缀和,和副对角线方向的斜缀和

根据每个点的坐标可以求出其主、副对角线方向攻击线与边界的交点,两个交点处的值的和 减去一个 坐标位置的值 即为答案

AC代码

#include<bits/stdc++.h>
using namespace std;

#define int long long

int n,m;
int mp[1003][1003];
int ri[1003][1003];
int li[1003][1003];
int xx;
int xcha,ycha;

int cul(int x,int y){
    int res = 0;
    xcha = n-x;
    ycha = m-y;
    if(xcha>=ycha){
        res += ri[x+ycha][m];
    }
    else{
        res += ri[n][y+xcha];
    }

    xcha = x-1;
    ycha = m-y;;
    if(xcha>=ycha){
        res += li[x-ycha][m];
    }
    else{
        res += li[1][y+xcha];
    }
    res -= mp[x][y];
    return res;
}

void makeri(int x,int y){
    ri[x][y]=mp[x][y];
    for(int i=x+1,j=y+1;i<=n && j<=m;++i,++j){
        ri[i][j]=mp[i][j]+ri[i-1][j-1];
    }
}

void makeli(int x,int y){
    li[x][y]=mp[x][y];
    for(int i=x-1,j=y+1;i>=1 && j<=m;--i,++j){
        li[i][j]=mp[i][j]+li[i+1][j-1];
    }
}

void solve(){
    cin>>n>>m;
    for(int i=1;i<=n;++i){
        for(int j=1;j<=m;++j) cin>>mp[i][j];
    }

    for(int i=1;i<=n;++i) makeri(i,1);
    for(int i=2;i<=m;++i) makeri(1,i);
    
    for(int i=1;i<=n;++i) makeli(i,1);
    for(int i=2;i<=m;++i) makeli(n,i);

    int res = 0;
    for(int i=1;i<=n;++i){
        for(int j=1;j<=m;++j){
            xx = cul(i,j);
            res = max(res,xx);
        }
    }
    cout<<res<<'\n';
}

signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int _=1;
    cin>>_;
    while(_--){
        solve();
    }
    return 0;
}

I-Tokitsukaze and Pajama Party_2025牛客寒假算法基础集训营4

思路

这题也是一个前缀和的变体,为 隔一个的前缀和

首先求出一个正常的前缀和hu[i]+=hu[i-1]

再通过 huu[i]=hu[i-2] 就得出了隔一个的前缀和

当遇到一个 uwawauwa 字串时,第一个u位置对应的huu[]的值即为这个字串能组成的歌词的数量

每个 uwawauwa 字串能组成的歌词的数量的和即为答案

AC代码

#include<bits/stdc++.h>
using namespace std;

#define int long long

string s;
int n;
int hu[100005];
int huu[100005];
string m = "uwawauwa";

void solve(){
    int res = 0;
   cin>>n;
   for(int i=0;i<=n+2;++i) hu[i]=0;
   cin>>s;
   for(int i=0;i<n;++i){
    if(s[i]=='u') hu[i]=1;
   }
   for(int i=1;i<n;++i) hu[i]+=hu[i-1];
    for(int i=2;i<n;++i) huu[i]=hu[i-2];

   //for(int i=0;i<n;++i) cout<<huu[i]<<" "; cout<<'\n';

    for(int i=2;i<=n-8;++i){ //cout<<"*"<<i<<" ";
        if(s[i]=='u' && s[i+1]=='w' && s[i+2]=='a' && s[i+3]=='w' 
        && s[i+4]=='a' && s[i+5]=='u' && s[i+6]=='w' && s[i+7]=='a'){ //cout<<"have "<<i<<" * ";
            res += huu[i];
        }
    }

    cout<<res<<'\n';
}

signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int _=1;
    cin>>_;
    while(_--){
        solve();
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值