AcWing第85场周赛

这场周赛是手速局hh

死或生

某国正在以投票的方式决定 2 名死刑犯(编号 1∼2)的生死。

共有 n 组人员(编号 1∼n)参与投票,每组 10 人。

每组成员只参与一名死刑犯的投票,其中第 i 组人员的投票对象是死刑犯 ti,其中 xi 人认为他无罪,yi 人认为他有罪。

在所有人员投票结束后,将对投票结果进行统计。

对于每名死刑犯,如果投他无罪的总票数大于或等于投他有罪的总票数,则他得以生还,否则他将被处死。

请你判断每名死刑犯的生死。

输入格式
第一行包含一个整数 n。

接下来 n 行,每行包含三个整数 ti,xi,yi。

保证两名犯人都会被投票。

输出格式
如果第一位死刑犯生还,则在第一行输出 LIVE,否则在第一行输出 DEAD。

如果第二位死刑犯生还,则在第二行输出 LIVE,否则在第二行输出 DEAD。

数据范围
32n10
2n10001ti20xi,yi10xi+yi=10

输入样例1:

2 1 5 5 2 6 4

输出样例1:

LIVE LIVE

输入样例2:

3 1 0 10 2 0 10 1 10 0

输出样例2:

LIVE DEAD

简单的枚举即可,借助哈希表记录无罪票数和有罪票数

#include <bits/stdc++.h> using namespace std; int a[3],b[3]; int n; int main() { cin>>n; while (n--) { int t,x,y; cin>>t>>x>>y; a[t]+=x; b[t]+=y; } for (int i=1;i<=2;i++) if (a[i]>=b[i]) puts("LIVE"); else puts("DEAD"); return 0; }

最大价值

已知,小写字母 a∼z 的价值分别为wa,wb,,wz

对于一个由小写字母构成的长度为 l 的字符串 S=s1,s2slws1×1+ws2×2++wsl×l

现在,给定一个由小写字母构成的字符串 S,请你在这个字符串中插入 k 个小写字母,要求最终得到的字符串的价值尽可能大。

注意:

  • 插入的位置可以随意选。
  • 插入的字母也可以随意选,可以插入不同字母。

输出最大可能价值。

输入格式
第一行包含一个字符串 S。

第二行包含一个整数 k。

第三行包含 26 个整数 wa,wb,,wz

输出格式
一个整数,表示最大可能价值。

数据范围
前 3 个测试点满足,S 的长度范围 [1,5]。
所有测试点满足,S 的长度范围 [1,1000],0k103wawz 的取值范围 [0,1000]。

输入样例:

abc 3 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

输出样例:

41

贪心即可,经过证明(很好证)要得到最大价值,插入方法即在尾部插入k个单个价值的最大的字母

#include <bits/stdc++.h> using namespace std; const int N = 1010; string s; int w[N]; int k; typedef long long LL; int main() { cin>>s>>k; int res=0; for (int i=0;i<26;i++) cin>>w[i],res=max(res,w[i]); LL ans=0; for (int i=0;i<s.size();i++) ans+=(w[s[i]-'a']*(i+1)); // 这里一定要记着-'a'啊!血的教训,这细节要注意! int len=s.size(); for (int i=len+1;i<=k+len;i++) { ans+=res*i; } cout<<ans<<endl; return 0; }

危险程度

有 n 种化学物质,编号 1∼n。

其中,有 m 对物质之间会发生反应。

现在,要将这些化学物质逐个倒入同一个试管之中,具体倒入顺序不限。

我们需要计算一下试管的危险值。

已知,空试管的危险值为 1,每倒入一种化学物质,如果该物质能够与之前倒入试管中的一种或多种物质发生反应,则试管的危险值将乘以 2。

请你计算并输出,通过合理安排所有化学物质的倒入顺序,能够得到的试管的最大危险值。

输入格式
第一行包含两个整数 n,m。

接下来 m 行,每行包含两个整数 x,y,表示化学物质 x 和化学物质 y 之间会发生反应。保证同一对化学物质在输入中最多出现一次。

输出格式
一个整数,表示最大危险值。

数据范围
前 4 个测试点满足 1n10
所有测试点满足 1n500mn(n1)21x<yn

输入样例1:

1 0

输出样例1:

1

输入样例2:

2 1 1 2

输出样例2:

2

输入样例3:

3 2 1 2 2 3

输出样例3:

4

题意中几个很重要的性质抓出来

  • 第一个放入的物品无法和其他物质反映,因为此时试管中没有其他物品
  • 不能相互反应的物品一定严格独立,没有交集
  • 假设同一个反应体系中的物品数为k个,则该反应体系对危险程度的贡献度为2k1,因此我们可以看出,每一个反应体系实际就是一个连通块,即每一个连通块中的物品数量为ki,则该连通块的作用即可为2ki1
    现在共有t个独立的连通块,则总的贡献度为2k11 * 2k21 * ... * 2ki1 = 2k1+k2+...+kit
    我们注意到共有n件物品,因此结果为2nt,题目瞬间转化为求解独立的连通块的数量

1. 法一:并查集求解独立连通块数量

#include <bits/stdc++.h> using namespace std; const int N = 55; int p[N]; int n,m; typedef long long LL; int find(int x) { if (p[x]!=x) p[x]=find(p[x]); return p[x]; } int main() { cin>>n>>m; for (int i=1;i<=n;i++) p[i]=i; int cnt=n; // cnt为独立集合的数量,即连通块的数量 while (m--) { int a,b; cin>>a>>b; int pa = find(a); int pb = find(b); if (pa!=pb) { cnt--; p[pa]=pb; } } printf("%lld\n",1ll<<n-cnt); return 0; }

2. 建图,图的遍历求解连通块的数量

dfs写法一

#include <bits/stdc++.h> using namespace std; const int N = 55; bool st[N],reaction[N][N]; int n,m; typedef long long LL; LL ans=1; void dfs(int x) { for (int i=1;i<=n;i++) { if (reaction[x][i]&&!st[i]) // 当前物品与1~i(除本身)有反应,即更新ans并顺着这个物品遍历 { ans<<=1; st[i]=true; dfs(i); } } } int main() { cin>>n>>m; while (m--) { int a,b; cin>>a>>b; reaction[a][b]=reaction[b][a]=true; } for (int i=1;i<=n;i++) { if (!st[i]) // 只要当前的物品还没有用过 { st[i]=true; dfs(i); } } cout<<ans<<endl; return 0; }

dfs写法二

#include <bits/stdc++.h> using namespace std; const int N = 55 ,M = N*N; // 无向图注意边数 int e[M],h[N],ne[M],idx; int n,m; typedef long long LL; bool st[N]; void add(int a,int b) // 经典邻接表建图add { e[idx]=b,ne[idx]=h[a],h[a]=idx++; } void dfs(int x) // dfs遍历无向图 { st[x]=true; for (int i=h[x];~i;i=ne[i]) { int j = e[i]; if (!st[j]) dfs(j); } } int main() { cin>>n>>m; memset(h,-1,sizeof h); // 不初始化的后果就是TLE while (m--) { int a,b; cin>>a>>b; add(a,b),add(b,a); // 无向图即为特殊的有向图 } int cnt=0; // 求解连通块的数量 for (int i=1;i<=n;i++) { if (!st[i]) { dfs(i); cnt++; } } cout<<(1ll<<n-cnt)<<endl; // 答案为2^{n-cnt} return 0; }

bfs写法

#include <bits/stdc++.h> using namespace std; const int N = 55 ,M = N*N; // 无向图注意边数 int e[M],h[N],ne[M],idx; int n,m; typedef long long LL; bool st[N]; queue<int>q; void add(int a,int b) // 经典邻接表建图add { e[idx]=b,ne[idx]=h[a],h[a]=idx++; } void bfs(int x) // bfs遍历无向图 { st[x]=true; q.push(x); // 借助stl while (q.size()) { auto t = q.front(); q.pop(); for (int i=h[t];~i;i=ne[i]) { int j = e[i]; if (!st[j]) { st[j]=true; q.push(j); } } } } int main() { cin>>n>>m; memset(h,-1,sizeof h); // 不初始化的后果就是TLE while (m--) { int a,b; cin>>a>>b; add(a,b),add(b,a); // 无向图即为特殊的有向图 } int cnt=0; // 求解连通块的数量 for (int i=1;i<=n;i++) { if (!st[i]) { bfs(i); cnt++; } } cout<<(1ll<<n-cnt)<<endl; // 答案为2^{n-cnt} return 0; }

总结

不论是并查集写法,还是dfs和bfs写法,本质都是求解图中的连通子块个数。
因此,我们对求解连通块个数的题型,即可采用并查集和图的遍历这两大类方法,其中图的遍历可以用dfs(代码简洁)或者bfs(思路简单,但借助队列实现代码量较为冗长)


这次确实不难,还是fw,继续努力吧~


__EOF__

本文作者dfl
本文链接https://www.cnblogs.com/sdnu-dfl/p/17035357.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   勇敢龙龙  阅读(52)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示