【2024秋#113】锦城ACM周测题解

【2024秋#112】锦城ACM周测题解
题目难度
F<B<C<D<E<A
A.推推选妃
思路
先全部遍历一遍,把每个组都遍历一遍存入map
- b1≠c1和 b2=c2和 b3=c3;
- b1=c1和 b2≠c2和 b3=c3;
- b1=c1和 b2=c2和 b3≠c3。
mp1用于存入一个组的第二个数和第三个数
mp2用于存入一个组的第一个数和第三个数
mp3用于存入一个组的第一个数和第二个数
mp4用于存入一个组
然后再遍历一次,ans加上所以符合条件的答案
注意
ans=ans+mp1[{op[i+1],op[i+2]}]+mp2[{op[i],op[i+2]}]+mp3[{op[i],op[i+1]}]-3-(mp4[{{op[i],op[i+1]},op[i+2]}]-1)* 3
因为自身这个组会为mp1,mp2,mp3都贡献了1,所以会减去3,然后考虑会有两个重复的组,就需要再减去(mp4[{{op[i],op[i+1]},op[i+2]}]-1)* 3
故最后求ans就变成了
ans=ans+mp1[{op[i+1],op[i+2]}]+mp2[{op[i],op[i+2]}]+mp3[{op[i],op[i+1]}]-(mp4[{{op[i],op[i+1]},op[i+2]}])* 3;
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void ac()
{
map<pair<int,int>,int>mp1;
map<pair<int,int>,int>mp2;
map<pair<int,int>,int>mp3;
map<pair<pair<int,int>,int>,int>mp4;
int n;
int op[200004];
cin>>n;
for(int i=1;i<=n;i++)
cin>>op[i];
for(int i=1;i<=n-2;i++)
{
mp1[{op[i+1],op[i+2]}]++;
mp2[{op[i],op[i+2]}]++;
mp3[{op[i],op[i+1]}]++;
mp4[{{op[i],op[i+1]},op[i+2]}]++;
}
ll ans=0;
for(int i=1;i<=n-2;i++)
ans=ans+mp1[{op[i+1],op[i+2]}]+mp2[{op[i],op[i+2]}]+mp3[{op[i],op[i+1]}]-(mp4[{{op[i],op[i+1]},op[i+2]}])*3;
cout<<ans/2<<endl;
}
int main()
{
int T;
cin>>T;
while(T--)
ac();
return 0;
}
B.推推想吃猫条
思路
每次后x除以y后,x最少都会减少一半,所以在logx次内一定会到达1。
到达后很好讨论,达到1前按题意模拟即可。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve()
{
ll x, y, k;
cin>>x>>y>>k;
ll a=y-(x%y);
while(k>0)
{
if(k>=a)
k-=a;
else
{
x+=k;
k=0;
break;
}
x+=a;
while(x%y==0)
{
x/=y;
if(x==1)
{
x+=(k%(y-1));
while(x%y==0)
x/=y;
k=0;
}
}
a=y-(x%y);
}
cout<<x<<endl;
}
int main()
{
int T;
cin>>T;
while(T--)
ac();
return 0;
}
C.op时间到
思路
先用vector存储无权的有向图,对每个学生分别bfs,用vis[i]表示第i个教室被遍历过多少次,最后只有vis[i]==k的教室满足条件。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll k,n,m,ans;
ll vis[1005];
ll v[1005];
vector<int>niu;
vector<int>op[1005];
void bfs(int x)
{
queue<int>q;
q.push(x);
bool v[1005];
memset(v,false,sizeof(v));
v[x]=true;
while(!q.empty())
{
int t=q.front();
q.pop();
for(int i=0;i<op[t].size();i++)
{
int to=op[t][i];
if(!v[to])
{
v[to]=true;
q.push(to);
vis[to]++;
}
}
}
}
int main()
{
cin>>k>>n>>m;
for(int i=1;i<=k;i++)
{
int t;cin>>t;
niu.push_back(t);
vis[t]++;
}
while(m--)
{
int a,b;
cin>>a>>b;
op[a].push_back(b);
}
for(int i=0;i<niu.size();i++)
bfs(niu[i]);
for(int i=1;i<=n;i++)
if(vis[i]==k)
ans++;
cout<<ans;
return 0;
}
D.推推是或运算大师
思路
首先求出n在二进制中有多少个1(例如7二进制为111有3个1)
__builtin_popcount()可以返回一个数二进制中1的个数
不过自己写的话也挺简单的hhh
如果只有1个1的话那答案就只有1个是他本身
否则有多个的话假设为k个
那么最终答案有k+1个数
用第四个样例来说
23的二进制为10111
然后23的前一个数和23的或运算为n,也就是把23的二进制的某个1变成0
就可以写出如下
7 19 21 22 23
分别是23减去1,2,4,16
23的二进制1 0 1 1 1
22的二进制1 0 1 1 0
21的二进制1 0 1 0 1
19的二进制1 0 0 1 1
7 的二进制0 0 1 1 1
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
void ac()
{
ll n;
cin>>n;
int ci=__builtin_popcountll(n);
if(ci==1)
{
cout<<1<<endl;
cout<<n<<endl;
}
else
{
cout<<ci+1<<endl;
for(int i=59;i>=0;i--)
{
if(n>>i&1)
cout<<(n^(1LL<<i))<<endl;
}
cout<<n<<endl;
}
}
int main()
{
int t;
cin>>t;
while(t--)
ac();
return 0;
}
E.推推打boss
方法1
用一个优先队列去存接下来进行的攻击,按照攻击下次使用的回合从小到大排序,一直跑得到答案
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll atk[200005];
ll cd[200005];
ll aatk[200005];
struct node
{
ll ATK;
ll CD;
ll xiaci;
bool operator<(const node &other) const
{
return xiaci>other.xiaci;
}
};
void solve()
{
ll hp,n;
cin>>hp>>n;
for(int i=1;i<=n;i++)
cin>>atk[i];
for(int i=1;i<=n;i++)
cin>>cd[i];
priority_queue<node>pq;
for(int i=1;i<=n;i++)
pq.push({atk[i],cd[i],1});
ll ans=1;
ll aatk=0;
while(hp>0)
{
vector<node>next;
aatk=0;
while(!pq.empty()&&pq.top().xiaci==ans)
{
auto node=pq.top();
pq.pop();
aatk+=node.ATK;
node.xiaci+=node.CD;
next.push_back(node);
}
if(aatk==0)
{
if(!pq.empty())
{
ans=pq.top().xiaci;
continue;
}
else
break;
}
hp-=aatk;
if(hp<=0)
break;
ans++;
for(auto &node:next)
pq.push(node);
}
cout<<ans<<endl;
}
int main()
{
int T;
cin>>T;
while(T--)
solve();
return 0;
}
方法2
先对题目进行分析,如果第k个回合能击败boss,那么k+1回合肯定也可以击败boss.
用二分去枚举回合数
点击查看代码
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
ll n,m;
ll a[200005],b[200005];
bool check(ll mid)
{
ll tot=0;
for(int i=1;i<=m;i++)
{
tot+=a[i]*(((mid-1)/b[i])+1);
if(tot>=n)
break;
}
if(tot>=n)
return true;
return false;
}
void ac()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
cin>>a[i];
for(int i=1;i<=m;i++)
cin>>b[i];
ll l=0,r=1e12,ans=-1;
while(l+1!=r)
{
ll mid=(l+r)/2;
if(check(mid))
{
ans=mid;
r=mid;
}
else
l=mid;
}
cout<<ans<<endl;
}
int main()
{
int t;
cin>>t;
while(t--)
ac();
return 0 ;
}
F.猜拳大赛
思路
如果总分不为2的倍数,则输出-1
否则就一直让分数最高的两个人减一分,直到无法操作
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll op[4];
ll ac()
{
int ans=0;
cin>>op[1]>>op[2]>>op[3];
if((op[1]+op[2]+op[3])%2!=0)
cout<<-1<<endl;
else
{
while(op[2]!=0&&op[3]!=0)
{
op[2]--;
op[3]--;
ans++;
sort(op+1,op+3+1);
}
cout<<ans<<endl;
}
}
int main()
{
int T;
cin>>T;
while(T--)
ac();
return 0;
}

qwqwq
浙公网安备 33010602011771号