Codeforces Round #704 (Div. 2)
A.三个游泳者
三个游泳者决定在游泳池办聚会!中午,他们从游泳池的最左边开始游。
第一个游泳者恰好花费\(a\)分钟游一个完整的来回,第二个恰好\(b\)分钟,第三个恰好\(c\)分钟。因此,第一个游泳者在游泳池最左边的时间点恰好为,\(0,a,2a,3a,...,\),第二个为\(0,b,2b,3b,...,\),第三个为\(0,c,2c,3c,...,\)
在他们开始游泳之后你恰好在\(p\)时刻达到游泳池的最左边。确定在其中一个游泳者抵达泳池最左边时你不得不等待多久。
输入
第一行包含一个单一的整数\(t(1\le t \le 1000)\)—测试用例数。下面\(t\)行包含测试用例的描述,每行一个
每行包含\(4\)个整数\(p,a,b,and\,c(1\le 10^{18})\),开始之后的时间,当你前往游泳池游泳的人游一个完整来回所需时间
输出
对于每一个样例,输出一个整数—在其中一个游泳者抵达泳池最左边时你需要等待多久
\(Example\)
略(见:https://codeforces.ml/contest/1492/problem/A)
注释:
在第一个样例中,第一个游泳者在\(0,5,10,15,...,\)分钟时抵达游泳池的最左边,第二个游泳者为\(0,4,8,12,...\),第三个为\(0,8,16,24,...\)。你在\(9\)分钟时抵达并且将在\(1\)分钟后遇到第一个游泳者。
在第二个样例中,第一个游泳者在\(0,6,12,18,...\),分钟是抵达游泳池的最左边,第二个游泳者为\(0,10,20,30,...\),第三个为\(0,9,18,27,...\)。你在\(2\)分钟时抵达并且将在\(4\)分钟后遇到第一个游泳者。
在第三个样例中,你在\(10\)分钟时抵达。与此同时,三个游泳者均在最左边。难得的好运气!
在第四个样例中,所有的游泳者抵达最左边均为\(0,9,18,27,...\)。你在\(10\)分钟时抵达并且在\(8\)分钟后遇到这三个游泳者。
代码:
#include <bits/stdc++.h>
using namespace std;
int main(int argc, char const *argv[])
{
long long t;
cin>>t;
while(t--)
{
long long ans = 2e18;
long long p,x;
cin>>p;
for (int i = 0; i < 3; ++i)
{
cin>>x;
long long y = p % x;
if (y != 0) x = x * (p/x+1) - p;
else x = x * (p/x) - p;
ans = min(ans,x);
}
cout<<ans<<endl;
}
return 0;
}
B.卡片组
你有一组\(n\)张牌,并且你不想再订购一个新的。
每张牌有一个在\(1\)到\(n\)之间的值\(p_i\) ,所有的\(p_i\)是成对的。牌组从下往上编号,\(p_1\)代表最下面一张,\(p_n\)代表代表最上面一张。
在每一步中你挑选一些整数\(k(k>0)\),从原来的牌组中拿走最上面的\(k\)张牌并放置它们,按照现在的顺序,放在新的牌组之上。你执行这个操作直到原来的牌组为空。(参考注意部分获得更好的理解)
让我们定义一个牌组的顺序为\(\sum^n_{i=1}n^{n-i}\cdot p_i\)
给出原来的牌组,输出使用上述操作后牌组最大可能顺序。
输入
第一行包含一个整数\(t(1\le t \le 1000)\)—测试用例数
每个测试用例的第一行包含一个整数\(n(1\le n \le 10^5)\)—你的牌组的大小
第二行包含\(n\)个整数\(p_1,p_2,...,p_n(1\le p_i\le n;p_i \neq p_j\,\,\,if\,\,\,i \neq j)\)—牌组中从下到上的牌的值
保证所有测试用例中\(n\)的和不超过\(10^5\)
输出
对于每个测试用例打印最大可能顺序。打印牌组从下到上的牌值。
如果有多个答案,打印其中任何一个。
\(Example\)
略(见:https://codeforces.ml/contest/1492/problem/B)
注释:
在第一个样例中,下面这个是最佳策略之一:
- 拿走\(p\)最上面的\(1\)张牌并且放到\(p^{'}\):\(p^{'}\)变为\([1,2,3]\),\(p^{'}\)变为\([4]\);
- 拿走\(p\)最上面的\(1\)张牌并且放到\(p^{'}\):\(p^{'}\)变为\([1,2]\),\(p^{'}\)变为\([4,3]\);
- 拿走\(p\)最上面的\(1\)张牌并且放到\(p^{'}\):\(p^{'}\)变为\([1]\),\(p^{'}\)变为\([4,3,2]\);
- 拿走\(p\)最上面的\(1\)张牌并且放到\(p^{'}\):\(p^{'}\)变为空,\(p^{'}\)变为\([4,3,2,1]\);
结果,\(p^{'}\)的顺序为\(4^3\cdot4+4^2\cdot3+4^1\cdot2+4^0\cdot1=256+48+8+1=313\)
在第二个样例中,最佳策略之一是:
- 拿走\(p\)最上面的\(4\)张牌并且放到\(p^{'}\):\(p^{'}\)变为\([1]\),\(p^{'}\)变为\([5,2,4,3]\);
- 拿走\(p\)最上面的\(1\)张牌并且放到\(p^{'}\):\(p^{'}\)变为空,\(p^{'}\)变为\([5,2,4,3,1]\);
结果,\(p^{'}\)的顺序为\(5^4\cdot5+5^3\cdot2+5^2\cdot4+5^1\cdot3+5^0\cdot1=3125+250+100+15+1=3491\)
在第三个样例中,最佳策略之一是:
- 拿走\(p\)最上面的\(2\)张牌并且放到\(p^{'}\):\(p^{'}\)变为\([4,2,5,3]\),\(p^{'}\)变为\([6,1]\);
- 拿走\(p\)最上面的\(2\)张牌并且放到\(p^{'}\):\(p^{'}\)变为\([4,2]\),\(p^{'}\)变为\([6,1,5,3]\);
- 拿走\(p\)最上面的\(2\)张牌并且放到\(p^{'}\):\(p^{'}\)变为空,\(p^{'}\)变为\([6,1,5,3,4,2]\);
结果,\(p^{'}\)的顺序为\(6^5\cdot6+6^4\cdot1+6^3\cdot5+6^2\cdot3+6^1\cdot4+6^0\cdot 2=46656+1296+1080+108+24+2=49166\)
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int a[N],b[N];
int main(int argc, char const *argv[])
{
int t;
cin>>t;
while(t--)
{
priority_queue<int,vector<int>,less<int> >q;
memset(a,0,sizeof a);
memset(b,0,sizeof b);
int n;
cin>>n;
for (int i = 1; i <= n; ++i)
{
cin>>a[i];
b[a[i]] = i;
q.push(a[i]);
}
while(!q.empty())
{
for (int i = b[q.top()]; i <= n ; ++i)
{
if (a[i] == 0) break;
cout<<a[i]<<" ";
a[i] = 0;
}
q.pop();
}
cout<<endl;
}
return 0;
}
思路:
每一次找到要输出的那一段数据里的最大值,向后依次输出。
使用一个数组\(a\)来存储输入数据,\(b\)来存储输入数据对应的下标,方便快速在\(a\)中找到该数据的位置。
使用优先队列来快速找到当前最大值,找到最大值后通过\(b\)数组找到当前最大值的下标,然后在\(a\)数组中将其后面的一一输出,并重置为\(0\),然后找下一个最大值,由于输出的数据都已经被重置为\(0\)只要下一个最大值被输出过就继续找下一个最大值,直到全部找完。
C.最大宽度
你不喜欢你的同学因为它令人厌烦,但是你尊重他的才智,有两个字符串:长度为\(n\)的字符串\(s\)和长度为\(m\)的字符串\(t\)。
对于一个序列\(p_1,p_2,...,p_m\)其中\(1\le p_1 < p_2 <...<p_m\le n\)如果对于所有的从\(1\)到\(m\)的\(i\)都有\(s_{pi}=t_i\)则称它为美丽的。序列的宽度定义为\(\max\limits_{1\le i<m}(p_{i+1}-p_i)\)
请帮助你的同学去确定美丽的序列的最大宽度。你的同学保证给你的两个序列至少有一个是美丽的。
输入
第一行包含两个整数\(n\)和\(m\)\((2\le m \le n\le2 \cdot10^5)\)—\(s\)和\(t\)的长度
下面一行包含一个长度为\(n\)的字符串\(s\),由小写的拉丁字母组成。
最后一行包含一个长度为\(m\)的字符串\(t\),由小写的拉丁字母组成。
可以保证给定的字符串有一个美丽序列。
输出
输出一个整数—美丽序列的最大宽度
\(Example\)
略(见:https://codeforces.ml/contest/1492/problem/C)
注释:
在第一个样例中有两个宽度为3的美丽序列:他们是\(\{1,2,5\}\)和\(\{1,4,5\}\)
在第二个样例中美丽序列的最大宽度是\(\{1,5\}\)
在第三个样例中恰好有一个美丽序列—它是\(\{1,2,3,4,5\}\)
在第四个样例中恰好有一个美丽序列—它是\(\{1,2\}\)
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
string s,t;
int a[N],b[N];
int main(int argc, char const *argv[])
{
int n,m;
cin>>n>>m;
cin>>s;
cin>>t;
int idx = 0;
for (int i = 0; i < n; ++i)
{
if (idx < m && s[i] == t[idx])
{
a[idx] = i;
idx++;
}
}
idx = m - 1;
for (int i = n - 1; i > 0; --i)
{
if (idx >= 0 && s[i] == t[idx])
{
b[idx] = i;
idx--;
}
}
int ans = 0;
for (int i = 1; i < m; ++i)
{
ans = max(ans,b[i] - a[i-1]);
}
cout<<ans<<endl;
return 0;
}
思路:
记录在\(s\)中最新找到\(t\)中每个字符的位置与最后找到的位置,作差即为答案。
D.天才的策略
给你三个整数\(a,b,k\)
找到满足下列条件的二进制形式的\(x\)和\(y\)\((x\ge y)\)
- \(x\)和\(y\)都包含\(a\)个\(0\)和\(b\)个\(1\)
- \(x-y\)(也是二进制形式)有恰好\(k\)个0
你不允许对\(x\)和\(y\)使用前导0
输入
只有一行包含3个整数\(a,b,k(0\le a;\,1\le b;\,0\le k \le a + b \le 2 \cdot 10^5)\)—0和1的个数,以及结果中1的个数。
输出
如果能找的合适的两个整数,打印\(''Yes''\)然后是二进制形式的\(x\)和\(y\)
否则打印\(’'No''\)
\(Example\)
略(见:https://codeforces.ml/contest/1492/problem/D)
注释:
在第一个样例中,\(x = 101000_2 = 2^5 + 2^3 = 40_{10},y = 100001_2 = 2 ^5 + 2 ^0 = 33_{10},\)\(40_{10}-33_{10}=7_{10}=2^2+2^1+2^0=111_2.Hence \,\,x-y\,\,has\,\,3\,\,ones\,\,in\,\,base-2.\)
在第二个样例中,\(x=10100_2=2^4+2^2=20_{10},\)\(y=10010_2=2^4+2^1=18,\)\(x-y=20-18=2_{10}=10_2.\)这恰好是一个1.
在第三个样例中,你可能会发现,要找到答案是不可能的。
代码:
#include <bits/stdc++.h>
using namespace std;
int main(int argc, char const *argv[])
{
int a,b,k,c;
cin>>a>>b>>k;
c = a + b - 2;
if (k == 0)
{
cout<<"Yes"<<endl;
for (int i = 0; i < b; ++i)
{
cout<<"1";
}
for (int i = 0; i < a; ++i)
{
cout<<"0";
}
cout<<endl;
for (int i = 0; i < b; ++i)
{
cout<<"1";
}
for (int i = 0; i < a; ++i)
{
cout<<"0";
}
}
else if (k <= c && a >= 1 && b >= 2)
{
a -= 1;
b -= 2;
k--;
cout<<"Yes"<<endl;
cout<<"11";
for (int i = 0; i < min(k,b); ++i)
{
cout<<"1";
}
if (k <= b)
{
for (int i = 0; i < a + 1; ++i)
{
cout<<"0";
}
for (int i = 0; i < b-k; ++i)
{
cout<<"1";
}
cout<<endl;
}
else
{
for (int i = 0; i < a + 1; ++i)
{
cout<<"0";
}
cout<<endl;
}
cout<<"10";
for (int i = 0; i < min(k,b); ++i)
{
cout<<"1";
}
if (k <= b)
{
cout<<"1";
for (int i = 0; i < a; ++i)
{
cout<<"0";
}
for (int i = 0; i < b-k; ++i)
{
cout<<"1";
}
cout<<endl;
}
else
{
for (int i = 0; i < k-b; ++i)
{
cout<<"0";
}
cout<<"1";
for (int i = 0; i < a-k+b; ++i)
{
cout<<"0";
}
cout<<endl;
}
}
else cout<<"No"<<endl;
return 0;
}
思路:
构造下面这个式子
11xxxxx0xxxxx
10xxxxx1xxxxx
分类讨论即可
E.几乎容错的数据库
你在数据库里存储了一个长度为\(m\)的数组。为了维护内部完整性和保护数据,数据库存储了\(n\)个该数组的副本。
不幸的是,最近发生的事件可能改变了数据库中每个副本存储的信息。
可以确定,在这一事件中每一个副本最多改变了两个元素。您需要根据数据库的当前状态来恢复为原始状态。
假如有多种可能还原数组,报告任何一个。如果没有一个数组与每个副本不超过两个不同元素,也要报告这一点。
输入
第一行包含整数\(n\)和\(m(2\le n;1\le m; n \cdot m \le 250\,000)\)—副本数量和数组大小
以下\(n\)行中的每一行描述了一个当前数据库存储的数组,它包含了\(m\)个整数\(s_{i,1},s_{i,2},...,s_{i,m}(1 \le s_{i,j} \le 10^9).\)
输出
如果存在一个数组与所有给定副本相符合,打印“Yes”然后是数组本身。这个数组长度必须为\(m\),并且只能包含1到\(10^9\)之间的整数
否则,打印“No”。
如果有多种可能打印他们中的任何一个。
\(Example\)
略(见:https://codeforces.ml/contest/1492/problem/E)
注释:
在第一个样例中,数组\([1,10,1,100]\)仅在一个位置上与第一个和第二个不同并且在两个位置上与第三个不同。
在第二个样例中,数组\([1,1,1,1,1,1,1]\)与第一个副本相同并且在最多两个位置上与其他所有副本不同。
在第三个样例中,每个数据库副本中最多有两个位置的数组是不同的。
代码:
//不会,参考网上题解写的(这个大佬的代码写得真好)
#include <bits/stdc++.h>
using namespace std;
const int N = 250001;
vector<int> v[N];
int ans[N];
int n,m,c;
bool check(int dep)
{
if (dep >= 3) return 0;
int ok = 1;
for (int i = 1; i < n; ++i)
{
vector<int> pos;
for (int j = 0; j < m; ++j)
{
if (v[i][j] != v[0][j]) pos.push_back(j);
}
int len = pos.size();
if (len > 4) return 0;
if (len < 3) continue;
if (dep == 2) return 0;
if (len == 3)
{
for (int j = 0; j < 3; ++j)
{
int org = v[0][pos[j]];
v[0][pos[j]] = v[i][pos[j]];
if (check(dep + 1))
{
ok = 1;
goto end;
}
v[0][pos[j]] = org;
}
ok = 0;
break;
}
else
{
for (int j = 0; j < 4; ++j)
{
int org = v[0][pos[j]];
v[0][pos[j]] = v[i][pos[j]];
if (check(dep + 1))
{
ok = 1;
goto end;
}
v[0][pos[j]] = org;
}
ok = 0;
break;
}
}
end:
if (ok) for (int i = 0; i < m; ++i) ans[i] = v[0][i];
return ok;
}
int main(int argc, char const *argv[])
{
cin>>n>>m;
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < m; ++j)
{
cin>>c;
v[i].push_back(c);
}
}
if (check(0))
{
cout<<"Yes"<<endl;
for (int i = 0; i < m; ++i) cout<<ans[i]<<" ";
}
else cout<<"No"<<endl;
return 0;
}
思路: