(年前年后的一块一起写了)(一些比较有意思的题在这里写一下)
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;
}