五题
C 小w和大W的决斗
链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
题目描述
小w和大W为了比出谁更聪明。决定进行一场游戏。游戏内容如下:
两人轮流操作,小w先进行操作,每次操作可以选择下列两个其一:
选择数组中的一个数x (x!=0),将xxx变成x−y(1≤y≤x)选择数组中的一个数x (x!=0),将其分成 i,j,k三个正整数 满足 i+j+k=x
先把数组全变为0的获胜。
请问小w是否有必胜策略。如果有输出"w win",否则输出"W win",(不带引号)。
输入描述:
第一行输入一个整数 n (1≤n≤104) 第二行输 nnn 个整数 a1 ... an (1≤ai≤100),用空格隔开.
输出描述:
输出一个字符串"w win" 或者 "W win"。
示例1
输入
3 1 1 3
输出
w win
示例2
输入
3 1 2 3
输出
W win
思路:
-----------------------------------------------------------------------------------------------------------------------------
sg 函数定义:
sg(x) 函数用于计算给定数 x 的 sg 值。sg 函数的具体定义规则是:
如果 x % 8 == 0,返回 x - 1。
如果 x % 8 == 7,返回 x + 1。
否则,返回 x 本身。
这种计算方式通常用于游戏理论中的Nim游戏或其变种,用来计算每一堆物品(如石子)的游戏价值。
计算游戏结果:
初始化 ans 为0,用来存储每一堆石子的 sg 值的异或结果。
使用 for 循环遍历 stones 向量中的每一堆石子数量,对每一堆石子的数量调用 sg 函数,然后将结果与 ans 进行异或操作。
利用游戏理论中的 sg 函数和Nim游戏的特性来确定最终的游戏结果,通过计算每一堆石子的 sg 值的异或结果来决定哪一方是必胜的。
代码:
#include <iostream>
#include <vector>
using namespace std;
// 计算某个数 x 的 sg 值
int sg(int x) {
if (x % 8 == 0) {
return x - 1;
} else if (x % 8 == 7) {
return x + 1;
} else {
return x;
}
}
int main() {
int n;
cin >> n; // 堆数
vector<int> stones(n);
// 输入每堆石子的数量
for (int i = 0; i < n; ++i) {
cin >> stones[i];
}
int ans = 0;
for (int stone : stones) {
ans ^= sg(stone);
}
if (ans == 0) {
cout<< "W win";
} else {
cout << "w win";
}
return 0;
}
运行结果:
D A*BBBB
链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
题目描述
集训队的longlong同学刚学完c语言基础后,非常高兴的开始做题,但是突然有一题难住了longlong同学。题面如下:
t组输入,每组输入给两个整数a和b,输出a∗b。但是a,b非常大,0<=a,b<=101000000。其中b的每一位都相同,且a,b都不含前导0。
longlong同学写不出题太痛苦了,于是请求你帮帮他。
输入描述:
第1行输入一个t表示t组输入(1<=t<=10)(
第2到2∗t+1行,每组给出两行,分别是a,b。1≤∣a∣,∣b∣≤1e6
∣a∣表示a的长度
输出描述:
输出t行,每行一个整数,表示a∗b的答案
示例1
输入
2 114514 22 1919810 33
输出
2519308 63353730
备注:
因为整数太大不能直接用py的乘法哦,要仔细思考一下哦。
思路1:
利用FFT实现多项式乘法,通过复数运算实现高效的大数乘法,并通过位运算和逆序处理来优化FFT的实现。
代码1:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#define ll long long
using namespace std;
const ll N=5000010;
const double pi=acos(-1);
string s;
bool b1;
ll n,m,limit,c[N];
struct complex{
double real,imag;
complex(double X=0,double Y=0){real=X; imag=Y;}
}a[N],b[N];
inline complex operator +(complex a,complex b){return complex(a.real+b.real,a.imag+b.imag);}
inline complex operator -(complex a,complex b){return complex(a.real-b.real,a.imag-b.imag);}
inline complex operator *(complex a,complex b){return complex(a.real*b.real-a.imag*b.imag,a.real*b.imag+a.imag*b.real);}
void FFT(complex *a,ll op){
for(ll i=0; i<limit; i++){
if(i<c[i]) swap(a[i],a[c[i]]);
}
for(ll mid=1; mid<limit; mid<<=1){
complex W(cos(pi/mid),op*sin(pi/mid));
for(ll r=mid<<1,j=0; j<limit; j+=r){
complex w(1,0);
for(ll l=0; l<mid; l++,w=w*W){
complex x=a[j+l],y=w*a[j+mid+l];
a[j+l]=x+y; a[j+mid+l]=x-y;
}
}
}
}
void solve_(){
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(c,0,sizeof(c));
cin>>s; n=s.size()-1;
for(ll i=0; i<=n; i++) a[n-i].real=s[i]-48;
cin>>s; m=s.size()-1;
for(ll i=0; i<=m; i++) b[m-i].real=s[i]-48;
limit=1; ll l=0;
while(limit<=n+m){
limit<<=1;
l++;
}
for(ll i=0; i<limit; i++) c[i]=(c[i>>1]>>1)|((i&1)<<(l-1));
FFT(a,1); FFT(b,1);
for(ll i=0; i<=limit; i++) a[i]=a[i]*b[i];
FFT(a,-1);
memset(c,0,sizeof(c));
for(ll i=0; i<=n+m+1; i++) c[i]=a[i].real/limit+0.5;
for(ll i=0; i<=n+m; i++){
c[i+1]+=c[i]/10;
c[i]%=10;
}
limit=n+m+1;
while(c[limit]){
c[limit+1]+=c[limit]/10;
c[limit]%=10;
limit++;
}
for(ll i=limit-1; i>=0; i--) {
if(c[i]==0&&b1==0)continue;
b1=1;
cout<<c[i];
}
if(b1==0)cout<<0;
cout << endl;
return ;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
int t;
cin >> t;
while(t--){
solve_();
}
return 0;
}
运行结果:
思路2: (题解)
代码2:
#include <iostream>
#include <cstring>
#include<algorithm>
using namespace std;
string a, b;
string ans;
void solve()
{
cin >> a >> b;
reverse(begin(a), end(a));
int c = 0, t = 0;
for (int i = 0; i < a.size() + b.size(); i++)
{
if (i < a.size())
c += a[i] - '0';
if (i >= b.size())
c -= a[i - b.size()] - '0';
t += c * (b[0] - '0');
ans.push_back(t % 10 + '0');
t /= 10;
}
while (ans.size() > 1 && ans.back() == '0')
ans.pop_back();
reverse(begin(ans), end(ans));
cout << ans << '\n';
}
int main()
{
int t;
cin >> t;
while (t--)
solve();
return 0;
}
E “好”字符
链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
题目描述
字符串的循环同构:表示把字符串的左边第一位移到最后一位,新串再进行这样的操作,得到的一些字符串都是原串的循环同构,例如"bcda"、"cdab"、"dabc"都是"abcd"的循环同构。
给你两个长度相等的字符串 a 和 b,字符串 a和 b均由小写字母组成。
当某个字符 x 在字符串 a 与字符串 b 或 b 的循环同构中出现的所有位置依次对应时(对于字符串的每一位,要么 a和 b 的这一位都是该字符,要么都不为该字符),称该字符为“好”字符。例如:当 a = "acba",b = "bdaa"时,字符'a'与字符'b'为“好”字符
现在,我们想请你判断字符串 a 与字符串 b 中“好”字符的数量
输入描述:
第一行给你一个 n (1≤n≤106),n 为字符串 a 与 b 的长度。 第二行与第三行为字符串 a和 b ,a 和 b 均由小写字母组成。
输出描述:
单行输出一个数字,表示“好”字符的个数。
示例1
输入
6 acabxb eababf
输出
2
说明
当b的循环同构体为"ababfe"时,字符'a'为“好”字符,当b的循环同构体为"feabab"时,字符'b'为“好”字符。
示例2
输入
3 abc cde
输出
1
思路:
统计字符串 ss1 在字符串 ss2 的多个周期中的出现次数,通过 KMP 算法实现部分匹配
代码:
#include <iostream>
#include <iomanip>
#include <vector>
#include <cstring>
#define int long long
using namespace std;
int n, ans;
string ss1, ss2;
int zm1[27], zm2[27]; // 字符统计数组,zm1 统计 ss1 中各字符出现次数,zm2 统计 ss2 中各字符出现次数
// 计算字符串的 Next 数组
vector<int> Next(string ss) {
vector<int> next;
next.push_back(0);
for (int i = 1, j = 0; i < ss.length(); i++) {
while (j > 0 && ss[j] != ss[i]) {
j = next[j - 1];
}
if (ss[j] == ss[i]) {
j++;
}
next.push_back(j);
}
return next;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
// 输入 n(字符串长度)、ss1(第一个字符串)、ss2(第二个字符串)
cin >> n >> ss1 >> ss2;
// 统计 ss1 中每个字符出现的次数
for (int i = 0; i < ss1.size(); i++) {
zm1[ss1[i] - 'a' + 1]++;
}
// 统计 ss2 中每个字符出现的次数
for (int i = 0; i < ss2.size(); i++) {
zm2[ss2[i] - 'a' + 1]++;
}
// 将 ss2 复制一份,形成双倍长度,用于后续匹配
ss2 = ss2 + ss2;
// 遍历每个字符,进行匹配操作
for (int i = 1; i <= 26; i++) {
if (zm1[i] != 0) { // 如果 ss1 中存在字符 i
if (zm2[i] == zm1[i]) { // 如果 ss2 中字符 i 的数量与 ss1 中相同
string ss3 = ss1, ss4 = ss2;
// 将 ss3 和 ss4 中不是字符 i 的位置替换为 '#'
for (int j = 0; j < ss3.size(); j++) {
if (ss3[j] - 'a' + 1 != i)
ss3[j] = '#';
}
for (int j = 0; j < ss4.size(); j++) {
if (ss4[j] - 'a' + 1 != i)
ss4[j] = '#';
}
// 使用 ss3 在 ss4 中进行 KMP 匹配
string ss = ss3, t = ss4;
vector<int> next = Next(ss);
for (int i = 0, j = 0; i < t.length(); i++) {
while (j > 0 && t[i] != ss[j]) {
j = next[j - 1];
}
if (t[i] == ss[j]) {
j++;
}
if (j == ss.length()) {
ans++;
break;
}
}
}
}
}
cout << ans << '\n';
return 0;
}
运行结果:
H 狼狼的备忘录
链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
题目描述
圈圈:代打王者,1r一局,3r3r3r 两局,5r 三局,不保证胜率,诚信经营
yq :圈,我赚的第一块是小寒冰的~
静静:鸢还不给我抽到张邈!!!
狼狼:瓦,启........
玉玉:长相思长相思长相思
以上是广告招租位,和本题无关。
由于狼狼是一个喜欢看星座的小女孩,为此她写了几本备忘录来记录集训队成员的星座信息。(桃白白说过,经常看星座相关的信息可以提高 codefoeces分数。多读书多看报,少吃零食多 codeforces!!!)
狼狼决定整理这几本备忘录里有关集训队成员的星座信息,每本备忘录都记录着一个成员的一条或多条星座信息。包含 n 本备忘录:每本备忘录都由一个名字 id 开头代表某个集训队成员名字,然后是一个记录的条目数代表狼狼在该备忘录中记录该成员的信息条数 op ,最后是 op 条星座信息本身。同一本备忘录中可能记录了多个相同的星座信息,不同的备忘录可能记录同一个人的多条信息。
狼狼认为整理这些备忘录信息应该遵循一下规则:
A:同一个成员的星座信息 x 是星座信息 y 的后缀,那么星座信息 x 会没有星座信息 y 完整,从而应该只保留星座信息 y,删除星座信息 x 。
B:同一个成员的星座信息可能以相同格式出现多次,那么只保留该信息一次。
注意:有可能两个不同的成员有着相同的星座信息,这是合法的。
现在狼狼现在要跟圈圈静静 yq 玉玉前往瓦的训练场,请你按照规则 A ,规则 B帮助狼狼整理她的备忘录,并且将这些备忘录信息按照字典序打印出来。
输入描述:
包含整数 n(1<=n<=20) 狼狼的备忘录本数 接下来的 n 行是按照语句中的格式对成员的星座信息进行描述。 成员名字是长度不超过10的非空字符串,他们仅由小写英文字母组成。条目数是一个不少于1,不超过10的整数。星座信息是长度不超过10的非空字符串,仅由数字组成。
输出描述:
打印有关狼狼备忘录中集训队成员们的星座信息。 第一行输出 m 表示在狼狼备忘录中找到的成员的数量。 以下 m 行必须包含以下格式:“成员名字 条目数信息 信息”,每条记录必须包含当前成员的所有星座信息。 输出时成员名字字典序小的先输出,每个成员的星座信息中字典序小的先输出,星座信息中前导零也要输出。
示例1
输入
3 karl 2 612 12 petr 1 12 katya 1 612
输出
3 karl 1 612 katya 1 612 petr 1 12
示例2
输入
4 ivan 3 123 123 456 ivan 2 456 456 ivan 8 789 3 23 6 56 9 89 2 dasha 2 23 789
输出
2 dasha 2 23 789 ivan 4 123 2 456 789
示例3
输入
2 yq 1 777777 icealsoheat 1 555
输出
2 icealsoheat 1 555 yq 1 777777
示例4
输入
10 zifei 1 8 zilatan 1 9 consuui 1 3 zonehawkr 1 10 wananan 1 5 foureyebird 1 4 xpp 1 7 wulong 1 6 andso 1 1 btqq 1 2
输出
10 andso 1 1 btqq 1 2 consuui 1 3 foureyebird 1 4 wananan 1 5 wulong 1 6 xpp 1 7 zifei 1 8 zilatan 1 9 zonehawkr 1 10
思路:
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
// 函数:判断字符串 a 是否为字符串 b 的后缀
bool f(const string& a, const string& b) {
if (a.size() > b.size()) {
return false;
}
return equal(a.rbegin(), a.rend(), b.rbegin()); // 判断 a 是否为 b 的后缀
}
// 函数:比较两个 pair<string, set<string>> 类型的对象,按照 first 字符串的字典序进行比较
bool cmp(pair<string, set<string>> a, pair<string, set<string>> b) {
return a.first < b.first;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int m;
cin >> m;
map<string, set<string>> mp;
while (m--) {
string name;
int c;
cin >> name >> c;
for (int i = 0; i < c; i++) {
string ss;
cin >> ss;
mp[name].insert(ss); // 将 ss 插入到对应名称 name 的集合中
}
}
// 将 map 转换为 vector,便于排序
vector<pair<string, set<string>>> ma(mp.begin(), mp.end());
sort(ma.begin(), ma.end(), cmp);
cout << mp.size() << endl;
for (const auto& i : ma) {
vector<string> namee;
// 将 set 中的元素复制到 vector 中
for (const auto& ss1 : i.second) {
namee.push_back(ss1);
}
// 对于 vector 中的每个元素,检查是否为其他元素的后缀并去重
for (int i = 0; i < namee.size(); i++) {
string t = namee[i];
for (int j = 0; j < namee.size(); j++) {
if (i != j && f(namee[i], namee[j])) {
namee.erase(namee.begin() + i); // 如果 namee[i] 是 namee[j] 的后缀,则删除 namee[i]
--i; // 删除后,调整 i 的位置
break;
}
}
}
cout << i.first << " " << namee.size() << " ";
for (const auto& ss1 : namee) {
cout << ss1 << " ";
}
cout << '\n';
}
return 0;
}
运行结果:
G lxy的通风报信
链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
题目描述
在 n × m 的平面星球里( 5<=n, m<=1000 ),存在着 a 支军队 ( 2<=a<=50) , 和 b 支驻扎在地图中的敌军 ( 0<=b<=n∗m−a ) ,你需要确保所有军队全部合并 .
军队坐标假设为 ( xi, yi) ,行军一天可以到达( xi , yi)或者 ( xi, yi ) 或者( xi, yi ) 或者 ( xi , yi ),在与另外一支军队相遇时自动合并到另一支军队,你的部队需要避免与敌军起冲突。
战事紧急,身为司令,你需要快速计算出最少多少天所有军队全部合并完毕,如果必须跟敌军发生冲突请输出 No ,否则输出最少的天数。
注意:
在一支军队移动时,其他军队不可移动。
军队合并是依次进行的,且起点和终点都必须是军队,起点终点不固定,每次行军,可以任意指定两个现存在地图中的不同军队作为起点终点。例如有三个军队A,B,C,A->B,A合并B,B再合并C。也可以A->B,C->B。
输入描述:
输入n ,m(5<=n, m<=1000) 输入n行,每行m列, 每列用* 表示军队 # 表示敌军 ... 表示可移动区域。
输出描述:
如果军队之间可以全部合并,输出需要多少步 反之输出No
示例1
输入
5 5 .#..# ..#*. *...* #.### .....
输出
6
说明
示例2
输入
5 5 ##### #.... ***## *#..# *####
输出
4
示例3
输入
5 5 .#*#. #.#.# ..*.. *.#.* .....
输出
No
思路:
代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e3 + 10;
int x[100], y[100];
int f[N][N], g[100][100];
int fa[N];
int n, m, it;
char s[N][N];
int wayy[4] = {0, 0, 1, -1};
int wayx[4] = {1, -1, 0, 0};
// BFS 函数:计算每个 '*' 到其他 '*' 的最短距离
void bfs(int id)
{
queue<pair<int, int>> q;
memset(f, -1, sizeof f);
q.push({x[id], y[id]});
f[x[id]][y[id]] = 0;
while (!q.empty())
{
auto [nx, ny] = q.front();
q.pop();
for (int i = 0; i < 4; i++)
{
int tx = nx + wayx[i], ty = ny + wayy[i];
if (tx <= 0 || tx > n || ty <= 0 || ty > m || s[tx][ty] == '#' || f[tx][ty] != -1)
continue;
q.push({tx, ty});
f[tx][ty] = f[nx][ny] + 1; // 更新距离
}
}
for (int i = 1; i <= it; i++)
{
if (f[x[i]][y[i]] == -1)
{
cout << "No\n";
exit(0);
}
g[id][i] = f[x[i]][y[i]]; // 存储每对 '*' 之间的距离
}
}
// 并查集函数:找到集合的根节点
int find(int x)
{
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
// 排序比较函数:按距离排序
bool cmp(tuple<int, int, int> a, tuple<int, int, int> b)
{
if (get<0>(a) != get<0>(b))
return get<0>(a) < get<0>(b);
if (get<1>(a) != get<1>(b))
return get<1>(a) < get<1>(b);
return get<2>(a) < get<2>(b);
}
void solve()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
cin >> s[i][j];
if (s[i][j] == '*')
it++, x[it] = i, y[it] = j;
}
}
for (int i = 1; i <= it; i++)
bfs(i);
for (int i = 1; i <= it; i++)
fa[i] = i;
vector<tuple<int, int, int>> ans;
for (int i = 1; i <= it; i++)
for (int j = 1; j <= it; j++)
ans.push_back({g[i][j], i, j});
sort(ans.begin(), ans.end(), cmp);
int res = 0;
// Kruskal 算法:构建最小生成树
for (auto [co, i, j] : ans)
{
if (find(i) == find(j))
continue;
res += co;
fa[find(j)] = find(i);
}
cout << res << '\n';
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t= 1;
while (t--)
solve();
return 0;
}