文章目录
- 写在前面
- A 装备二选一(一)
- 思路
- code
- B 百变吗喽
- 思路
- code
- C 16进制世界
- 思路
- code
- D 四散而逃
- 思路
- code
- F 追寻光的方向
- 思路
- code
- G 等公交车
- 思路
- code
- H 24点
- 思路
- code
- I 正义从不打背身
- 思路
- code
- L koala的程序
- 思路
- code
河南萌新联赛2024第(六)场:郑州大学
写在前面
昨天打的这场萌新联赛打的也是非常烂,感觉最近不在状态,打比赛总感觉发挥不出自己的水平,可能是集训一个多月都没咋休息了,身心也是有点疲惫,在坚持几天就开学了,在这最后几天在加把劲,多学一点东西总归是好的(菜是原罪QWQ)
A 装备二选一(一)
思路
签到题,假设普通攻击为1并且攻击木桩100次,那么它产生暴击的次数就为 a ∣ c a | c a∣c 次,比较两次的伤害即可
code
void solve(){
int a,b,c,d;
cin >> a >> b >> c >> d;
int ans1=a*b+(100-a);
int ans2=c*d+(100-c);
if(ans2>ans1) cout << "YES";
else cout << "NO";
return ;
}
B 百变吗喽
思路
考点:模拟
对于字符串s来说,它只可能比字符串t少一个字符
首先先找出字符串s缺少的字符(若缺少的字符超过1,那么字符串s就不可能等于字符串t,直接输出0)
找到该字符以及该字符在t的位置,这时需要判断它的左右两边是否为相同的字符,若满足则继续判断
每次都将满足的字符的下标存入ans数组里面,最后将ans进行升序排序,输出ans即可
code
void solve(){
vector<int> ans;
int k=-1;
string s,t;
cin >> s >> t;
char c;
for(int i=0;i<s.size();++i){
if(s[i]!=t[i]){
k=i;
c=t[i];
for(int j=i+1;j<t.size();++j){
if(t[j]!=s[j-1]){
cout << 0 << endl;
return ;
}
}
break;
}
}
if(k==-1){
c=t[t.size()-1];
k=t.size()-1;
}
ans.push_back(k);
int k1=k;
while(k>0){
k--;
if(t[k]==c) ans.push_back(k);
else break;
}
while(k1<t.size()-1){
k1++;
if(t[k1]==c) ans.push_back(k1);
else break;
}
sort(ans.begin(),ans.end());
cout << ans.size() << endl;
for(auto i : ans) cout << i << " " << c << endl;
return ;
}
C 16进制世界
思路
考点:背包DP
题目要求月饼的幸福度之和为16的倍数,那么它的价值就为当前幸福度求余16
定义
f
[
j
]
[
k
]
f[j][k]
f[j][k] 表示当前背包容量为j并且幸福度为k时,它所容纳的月饼数
显然
f
[
1
−
m
]
[
0
]
f[1-m][0]
f[1−m][0] 是我们答案的区间
考虑状态转移,当前幸福度为k时,它可以由(k+w)%16而来
说明上一步的余数k加上当前幸福度w对16的求余刚好等于k
那么它的状态转移方程就为
f
[
j
]
[
k
]
=
m
a
x
(
f
[
j
]
[
k
]
,
f
[
j
−
v
]
[
(
k
+
w
)
%
16
]
+
1
)
f[j][k]=max(f[j][k],f[j-v][(k+w)\%16]+1)
f[j][k]=max(f[j][k],f[j−v][(k+w)%16]+1)
这是填表法的做法,还有另一种刷表法的做法:
f
[
j
]
[
(
k
+
w
)
%
16
]
=
m
a
x
(
f
[
j
]
[
(
k
+
w
)
%
16
]
,
f
[
j
−
v
]
[
k
]
+
1
)
f[j][(k+w)\%16]=max(f[j][(k+w)\%16],f[j-v][k]+1)
f[j][(k+w)%16]=max(f[j][(k+w)%16],f[j−v][k]+1)
code
void solve(){
int n,m;
cin >> n >> m;
vector<vector<int>> f(m+1,vector<int>(16,-inf));
f[0][0]=0;//初始值
for(int i=1;i<=n;++i){
int v,w;
cin >> v >> w;
w%=16;
for(int j=m;j>=v;--j)
for(int k=0;k<=15;++k){
f[j][k]=max(f[j][k],f[j-v][(k+w)%16]+1);// 填表法
// f[j][(k+w)%16]=max(f[j][(k+w)%16],f[j-v][k]+1); 刷表法
}
}
int ans=0;
for(int j=0;j<=m;++j) ans=max(ans,f[j][0]);
cout << ans << endl;
return ;
}
D 四散而逃
思路
考点:模拟
把玩一下可以发现,只要序列中的数不全为1,那么所有人都可以逃出去(n=3需要特判,若中间的数为奇数则不满足)
我们只需要遍历一遍数组,若当前数为偶数,那么它需要的操作数就为
a
[
i
]
/
2
a[i]/2
a[i]/2
若当前数为奇数,那么它需要的操作数就为
a
[
i
]
/
2
+
1
a[i]/2+1
a[i]/2+1
两者结合,则为
(
a
[
i
]
+
1
)
/
2
(a[i]+1)/2
(a[i]+1)/2
code
int a[N];
void solve(){
int n;cin >> n;
for(int i=1;i<=n;++i) cin >> a[i];
if(n==3 && a[2]%2){
cout << -1 << endl;
return ;
}
int flag=1;
for(int i=2;i<n;++i){
if(a[i]!=1) flag=0;
}
if(flag){
cout << -1 << endl;
return ;
}
int ans=0;
for(int i=2;i<n;++i) ans+=(a[i]+1)/2;
cout << ans << endl;
return ;
}
F 追寻光的方向
思路
考点:模拟
签到题,遍历一遍数组,每次遍历到当前序列的最大值时,ans++,将前面的序列的数都减去,继续循环,直到循环结束,输出ans
code
int a[N],b[N];
map<int,int> m;
void solve(){
int n;cin >> n;
for(int i=1;i<=n;++i){
cin >> a[i];
b[i]=a[i];
m[a[i]]++;
}
int ans=0;
sort(b+1,b+1+n,greater<int>());
int l=1;
if(b[1]==a[1]) l++;
m[a[1]]--;
for(int i=2;i<n;++i){
if(a[i]==b[l]){
ans++;
}
m[a[i]]--;
while(m[b[l]]==0 && l<=n) l++;
}
cout << ans;
return ;
}
G 等公交车
思路
考点:贪心+二分
对于每次询问,首先判断最后一班车出发的时间加上到x站的时间是否小于等于t,不满足直接输出TNT
满足则将当时时间t减去到x站的时间,令这个值为d,相当于在出发点查找与之匹配的公交车
查找可以用二分来优化,查找第一个大于等于d的值,然后进行相减即是所需要的时间
code
int a[N],b[N],sum[N];
void solve(){
int n,m;
cin >> n >> m;
for(int i=1;i<=n;++i){
cin >> a[i];
}
for(int i=1;i<=m;++i) cin >> b[i];
int q;cin >> q;
while(q--){
int t,x;
cin >> t >> x;
if(b[m]+a[x]<t) cout << "TNT" << endl;
else{
t-=a[x];
int k=lower_bound(b+1,b+1+m,t)-b;
cout << b[k]-t << endl;
}
}
return ;
}
H 24点
思路
考点:模拟
对于这四个数,将它们所有的情况进行排列枚举,由于题目有除法,必然会有精度损失,最后需要判断一下当前的值减去24的绝对值是否小于等于1e-6 即可
code
int a[N],flag=0;
unordered_map<string, int> m = {
{"A", 1}, {"2", 2}, {"3", 3}, {"4", 4}, {"5", 5},
{"6", 6}, {"7", 7}, {"8", 8}, {"9", 9}, {"10", 10},
{"J", 11}, {"Q", 12}, {"K", 13}
};
void dfs(vector<double> a){
if(flag) return ;
if(a.size()==1){
if(abs(a[0]-24)<=1e-6) flag=1;
return ;
}
for(int i=0;i<a.size();++i)
for(int j=0;j<a.size();++j){
if(i==j) continue;
vector<double> q=a;
q[j]=q[i]+q[j];
q.erase(q.begin()+i);
dfs(q);
q=a;
q[j]=q[i]-q[j];
q.erase(q.begin()+i);
dfs(q);
q=a;
q[j]=q[i]*q[j];
q.erase(q.begin()+i);
dfs(q);
if(q[j]){
q=a;
q[j]=q[i]/q[j];
q.erase(q.begin()+i);
dfs(q);
}
}
return ;
}
void solve(){
vector<double> a;
for(int i=0;i<4;++i){
string s;cin >> s;
a.push_back(m[s]);
}
flag=0;
dfs(a);
if(flag) cout << "YES" << endl;
else cout << "NO" << endl;
return ;
}
I 正义从不打背身
思路
考点:模拟?
这题需要打表找规律,如图:
假设初始状态为 ‘-’
很明显,如果m为奇数,奇数项的数在前面,并且它的状态发生改变,偶数项的数在后面,且状态不变
m为偶数,偶数项的数在前面,状态发生改变,奇数项在后面,状态不变
对于超出m的项数,那自然也是不变的
我们只需要按规律模拟即可
code
void solve(){
int n,m;
cin >> n >> m;
string s;cin >> s;
for(auto &i : s){
if(i=='P') i='1';
else i='0';
}
s=' '+s;
string s1=s;
int l=1,r=m;
if(m & 1){
for(int i=m;i>=1;--i){
if(i & 1) s1[l]=(s[i]=='1'?'0':'1'),l++;
else s1[r]=(s[i]=='1'?'1':'0'),r--;
}
}
else{
for(int i=m;i>=1;--i){
if(!(i & 1)) s1[l]=(s[i]=='1'?'0':'1'),l++;
else s1[r]=(s[i]=='1'?'1':'0'),r--;
}
}
for(int i=1;i<=n;++i) cout << s1[i] << " ";
return ;
}
L koala的程序
思路
考点:树状数组 or 线段树
对于这题来说,只需要单点查询,用树状数组更为方便
对于题目的代码和样例,我们不难看出这是一道约瑟夫问题
再看一眼n和k的数据范围,3e5,直接用朴素的算法是过不了的,时间复杂度大致为
O
(
n
2
)
O(n^2)
O(n2)
一看这数据范围自然得用
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)的算法来做,很显然想到树状数组
对于每次选取的位置,我们能想到
(
n
o
w
+
m
−
1
)
%
n
(now+m-1)\%n
(now+m−1)%n (now就是当前位置,m为第几个人出队,n为总人数)
但是这样选取,我们无法取到n的位置
假设一个序列 1 2 3 ,m=3
now初始值为1,(1+3-1)% 3 =0
那么我们可以在取模时做一些技巧,即
(
n
o
w
−
1
+
m
−
1
)
%
n
+
1
(now-1+m-1)\%n+1
(now−1+m−1)%n+1 ,进行一次+1 -1 的操作,这样就能取到n了
每次选取到的数,对于树状数组来说,代表的是前缀和的值(具体讲解看下图)
具体实现看代码~~
code
int a[N];
int n,m;
void add(int x,int k){
for(int i=x;i<=n;i+=lowbit(i)){
a[i]+=k;
}
}
int sum(int x){
int ans=0;
for(int i=x;i;i-=lowbit(i)){
ans+=a[i];
}
return ans;
}
int search(int s){
int l=1,r=n;
while(l<r){
int mid=l+r>>1;
if(sum(mid)>=s) r=mid;
else l=mid+1;
}
return l;
}
void solve(){
cin >> n >> m;
for(int i=1;i<=n;++i) add(i,1);
int now=1,op=n;
while(op>1){
now=(now-1+m-1)%op+1;
int ans=search(now);
cout << ans << " ";
add(ans,-1);
op--;
}
return ;
}