Codeforces Round #760
比赛链接
Codeforces Round #760 (Div. 3)
A. Polycarp and Sums of Subsequences
题意
有一个由3个正整数组成的数组a。他写出这个数组中所有非空子序列的和,按非降序排序,得到一个包含7个整数的数组b。
即给定一个有7个元素的集合B,问是否存在一个三个数的集合A
{
a
,
b
,
c
}
{\{a,b,c\}}
{a,b,c},满足
{
a
,
b
,
c
,
a
+
b
,
a
+
c
,
b
+
c
,
a
+
b
+
c
}
{\{a,b,c,a+b,a+c,b+c,a+b+c\}}
{a,b,c,a+b,a+c,b+c,a+b+c}等于集合B。
思路
首先得确定集合A中
a
,
b
,
c
{a,b,c}
a,b,c的值。假定
a
<
b
<
c
a<b<c
a<b<c,将集合B排好序后,则集合B中前两个数一定是
a
,
b
a,b
a,b,
c
c
c存在于B中第三、四个数。做两次验证即可。
AC代码
#include <bits/stdc++.h>
using namespace std;
int a[10];
int main(){
int T; cin>>T;
while(T--){
for(int i=0; i<7; i++) cin>>a[i];
sort(a, a+7);
cout<<a[0]<<" "<<a[1]<<" ";
if(a[0] + a[1] == a[2]) cout<<a[3];
else cout<<a[2];
puts("");
}
}
B. Missing Bigram
题意
Polycarp推出了一款新游戏供您玩。他称之为“丢失的二元图”。
一个单词的双字母表是其中两个相邻字母的序列。
例如,单词“abbaaba”包含双字母“ab”、“bb”、“ba”、“aa”、“ab”和“ba”。
游戏如下。首先,Polycarp提出了一个单词,仅由小写字母“a”和“b”组成。然后,他在白板上按单词出现的顺序写下所有的大字。之后,他从白板上擦去其中一个。
最后,Polycarp邀请您猜测他想出的单词是什么。
你的目标是找到任何一个单词,这样就可以写下它的所有双字图并删除其中一个,这样产生的双字图序列就和Polycarp最后的一个一样。
测试是以答案存在的方式生成的。如果有多个答案,您可以打印其中任何一个。
即给出
n
−
2
n-2
n−2个相邻字符串,构造长度为
n
n
n的字符串,这个字符串只包含’a’,‘b’。
思路
对于第i个相邻字符串,如果第i-1的尾与第i个的头的字符相同,那只添加第i个的尾字符到目标字符串。将多的位置全部补’a’。
AC代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int main(){
int T; cin>>T;
while(T--){
int n; cin>>n;
string ans = "";
for(int i=1; i<=n-2; i++){
string s; cin>>s;
if(ans[ans.size()-1] == s[0]) ans += s[1];
else ans += s;
}
for(int i=1; i<=n-ans.size(); i++) ans += 'a';
cout<<ans<<endl;
}
}
C. Paint the Array
题意
您将得到一个由n个正整数组成的数组a。您必须选择一个正整数d并将所有元素绘制为两种颜色。所有可被d整除的元素将被涂成红色,所有其他元素将被涂成蓝色。
如果数组中没有具有相同颜色的相邻元素对,则称为“美丽”。你的任务是找到任何能产生美丽色彩的d值,或者报告这是不可能的。
思路
想让数组没有相同颜色的相邻元素,只有可能红色的为奇数下标,蓝色的为偶数下标、或者红色的为偶数下标,蓝色的为奇数下标。
先取奇数下标区域的最大公约数t,看偶数下标有没有元素能整除他。偶数下标区域同理。
AC代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll a[105];
int n;
ll gcd(ll a, ll b){
return b?gcd(b, a%b):a;
}
ll solve(){
ll t = a[1];
for(int i=3; i<=n; i+=2) t = gcd(t, a[i]);
for(int i=2; i<=n; i+=2){
if(a[i]%t==0) goto loop;
}
return t;
loop:;
t = a[2];
for(int i=4; i<=n; i+=2) t = gcd(t, a[i]);
for(int i=1; i<=n; i+=2){
if(a[i]%t==0) return 0;
}
return t;
}
int main(){
int T; cin>>T;
while(T--){
cin>>n;
for(int i=1; i<=n; i++) cin>>a[i];
ll ans = solve();
cout<<ans<<endl;
}
}
D. Array and Operations
题意
给你一个n整数的数组a和另一个整数k,保证2k≤n。
您必须使用此数组执行完全k操作。在一个操作中,您必须选择数组的两个元素(让它们成为
a
i
a_i
ai和
a
j
a_j
aj;它们可以相等或不同,但它们在数组中的位置不能相同),将它们从数组中删除,然后添加
⌊
a
i
a
j
⌋
⌊\dfrac{a_i}{a_j}⌋
⌊ajai⌋ 按你的计分。
最初,你的分数是0。完全执行k操作后,将数组的所有剩余元素添加到分数中。
计算你可能得到的最低分数。
思路
贪心,尽量让
a
i
<
a
j
a_i<a_j
ai<aj,这样分数为0。排完序后将最后k个当做
a
j
a_j
aj,则第
a
j
−
k
a_{j-k}
aj−k作为
a
i
a_i
ai。前n-2*k就是剩余元素。
AC代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll a[105];
int q[200005];
int n, k;
int ans;
int main(){
int T; cin>>T;
while(T--){
cin>>n>>k;
for(int i=1; i<=n; i++) cin>>a[i];
sort(a+1, a+n+1);
int ans = 0;
for(int i=1; i<=n-2*k; i++) ans += a[i];
for(int i=1; i<=k; i++) ans += !(a[n-k+i] > a[n-2*k+i]);
cout<<ans<<endl;
}
}
E. Singers’ Tour
题意
n城镇按顺序排列成一个圆圈。城镇按顺时针顺序从1到n编号。在第i个镇上,住着一位歌手,每一位歌手都有一套
a
i
a_i
ai分钟的曲目i∈[1,n]。
每个歌手都以顺时针顺序访问了所有n城镇,从他居住的城镇开始,并在每个城镇举办了一场音乐会。此外,在每个镇上,第i位歌手都受到了启发,创作了一首持续时间为
a
i
a_i
ai分钟的歌曲。这首歌被添加到他的曲目中,以便他能在其他城市演唱。
因此,对于第i位歌手来说,在第i个镇的音乐会将持续i分钟,在第(i+1)个镇的音乐会将持续
2
∗
a
i
2*a_i
2∗ai分钟……,在第
(
(
i
+
k
)
m
o
d
n
+
1
)
((i+k)mod n+1)
((i+k)modn+1)个镇,音乐会的持续时间为
(
k
+
2
)
∗
a
i
(k+2)*a_i
(k+2)∗ai,在镇上
(
(
i
+
n
−
2
)
m
o
d
n
+
1
)
−
n
∗
a
i
((i+n−2) mod n+1)-n*a_i
((i+n−2)modn+1)−n∗ai分钟。
您将获得一个b整数数组,其中
b
i
b_i
bi是第i镇音乐会的总持续时间。重建任何正确的正整数序列a或说这是不可能的。
思路
把每个城市的多项式列出来,进行多项式计算即可。
AC代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int M = 4e4+5;
ll a[M], ans[M];
int n;
bool solve(){
ll sum = 0, tot = 0;
for(int i=1; i<=n; i++){
cin>>a[i];
sum += a[i];
tot += i;
}
if(sum % tot) return 0;
sum /= tot;
for(int i=1; i<=n; i++){
ll t = a[i] + sum - a[i%n+1];
if(t <= 0 || t % n) return 0;
ans[i%n+1] = t/n;
}
return 1;
}
int main(){
int T; cin>>T;
while(T--){
cin>>n;
if(solve()){
puts("YES");
for(int i=1; i<=n; i++) cout<<ans[i]<<" ";
puts("");
}
else puts("NO");
}
}
F. Reverse
题意
给你两个正整数x和y。您可以使用x执行以下操作:以二进制形式写入,不带前导零,在其右侧添加0或1,反转二进制形式,并将其转换为十进制数,作为x的新值。
您的任务是找出经过一定数量的操作(可能为零)后,x是否可以变成y。
思路
如果x==y,则直接返回true。
如果x!=y,则至少要变换一次。如果变换一次,则x的二进制前后最后一位必须为’1’。
如果x二进制表示为1abc1,则y必须为:若干个’1’ + 1abc1 + 若干个’1’ || 若干个’1’ + 1cba1 + 若干个’1’。
AC代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll x, y;
string a, tao, b;
string str(ll num){
string s = "";
while(num){
s += '0' + (num&1);
num >>= 1;
}
return s;
}
bool cmp(string a, string b){
int k = b.find(a);
if(~k){
for(int i=0; i<k; i++) if(b[i] == '0') goto loop1;
for(int i=k+a.size(); i<b.size(); i++) if(b[i] == '0') goto loop1;
return 1;
}
loop1:;
reverse(a.begin(), a.end());
k = b.find(a);
if(~k){
for(int i=0; i<k; i++) if(b[i] == '0') goto loop2;
for(int i=k+a.size(); i<b.size(); i++) if(b[i] == '0') goto loop2;
return 1;
}
loop2:;
return 0;
}
bool solve(){
if(x == y) return 1;
a = '1'+str(x);
b = str(y);
if(cmp(a, b)) return 1;
ll t = 0;
while(x){
t <<= 1;
t += x&1;
x >>= 1;
}
if(cmp(str(t), b)) return 1;
return 0;
}
int main(){
while(cin>>x>>y){
if(solve()) puts("YES");
else puts("NO");
}
}
G. Trader Problem
题意
你手里有n个物品,第i个价值为
a
i
a_i
ai。另有m个物品,第i个价值为
b
i
b_i
bi。你可以把你手里的价值为x的物品与不属于你的价值不超过x + k的物品进行交换,交换后原本属于你的物品不再属于你并且可以在新的交换中被换回来,而作为交换,原本不属于你的那个物品现在属于你并且可以被用于新的交换中。现在有q次询问,给定k,求经过任意次数交换后属于你的物品的价值的最大值。
思路
可以把所有数字放到一起排序,那么如果一个数字可以达到第一个比他大的数字就连边。
然后数字序列就构成了若干个连通块,每一个连通块的贡献就是前 num[i] 大的数字和。
这里 num[i] 代表这个连通块中包含初始数字的个数。
这个模型可以将询问离线从小到大排序,然后挨个合并并查集即可。
AC代码
附上cf官方AC代码……
#include <bits/stdc++.h>
using namespace std;
#define forn(i, n) for(int i = 0; i < int(n); i++)
const int N = 400 * 1000 + 13;
int n, m, k;
int a[N], b[N];
int q[N];
int p[N];
multiset<int> wst[N], bst[N];
long long sum;
int getp(int a){
return a == p[a] ? a : p[a] = getp(p[a]);
}
void unite(int a, int b){
a = getp(a), b = getp(b);
if (wst[a].size() + bst[a].size() < wst[b].size() + bst[b].size()) swap(a, b);
for (auto it : wst[b])
wst[a].insert(it);
for (auto it : bst[b])
bst[a].insert(it);
wst[b].clear();
bst[b].clear();
while (!bst[a].empty() && !wst[a].empty() && *bst[a].begin() < *wst[a].rbegin()){
sum -= *bst[a].begin();
sum += *wst[a].rbegin();
bst[a].insert(*wst[a].rbegin());
wst[a].insert(*bst[a].begin());
bst[a].erase(bst[a].begin());
wst[a].erase(--wst[a].end());
}
p[b] = a;
}
long long ans[N];
struct event{
int x, t, i;
};
int main() {
scanf("%d%d%d", &n, &m, &k);
forn(i, n)
scanf("%d", &a[i]);
forn(i, m)
scanf("%d", &b[i]);
forn(i, k)
scanf("%d", &q[i]);
vector<pair<int, int>> tot;
forn(i, n) tot.push_back({a[i], 1});
forn(i, m) tot.push_back({b[i], 0});
sort(tot.begin(), tot.end());
sum = accumulate(a, a + n, 0ll);
forn(i, n + m){
p[i] = i;
wst[i].clear();
bst[i].clear();
if (tot[i].second) bst[i].insert(tot[i].first);
else wst[i].insert(tot[i].first);
}
vector<event> ev;
forn(i, n + m - 1) ev.push_back({tot[i + 1].first - tot[i].first, 0, i});
forn(i, k) ev.push_back({q[i], 1, i});
sort(ev.begin(), ev.end(), [](const event &a, const event &b){
if (a.x != b.x) return a.x < b.x;
return a.t < b.t;
});
for (auto it : ev){
if (it.t == 0)
unite(it.i, it.i + 1);
else
ans[it.i] = sum;
}
forn(i, k) printf("%lld\n", ans[i]);
}