Codeforces Round #713 (Div. 3)题解
题目链接
这一场打的中规中矩吧,毕竟人均五题。
A题
题意:给你一个数组,数组中只有两种数值,找出只出现一次的数值的下标。
思路:显然只有整个字符串全是'a'才无解,否则对字符串进行扫描,对称位置不是'a'的地方放'a'即可。
代码如下
int n;
int a[N];
map<int, int> mp;
int main()
{
IOS;
int T;
cin >> T;
while(T --)
{
mp.clear();
cin >> n;
for(int i = 1 ; i <= n ; i ++)
{
cin >> a[i];
mp[a[i]] ++;
}
int num;
for(auto x : mp)
if(x.y == 1)
{
num = x.x;
break;
}
for(int i = 1 ; i <= n ; i ++)
if(num == a[i])
{
cout << i << endl;
break;
}
}
return 0;
}
B题
这题我开始看错题了,以为要构造的是正方形,wa了两发,我真sb。
题意:给你一个\(n * n\)的图, 其中有两个''符号,让你构造一个边与图的边平行的矩形,''位于该矩形的四个顶点上。
思路:显然只需要根据给出的两个点的位置,分类判断一下。
代码如下
int n;
char g[N][N];
int main()
{
IOS;
int T;
cin >> T;
while(T --)
{
cin >> n;
for(int i = 1 ; i <= n ; i ++)
cin >> g[i] + 1;
int a, b, c, d;
int cnt = 0;
for(int i = 1 ; i <= n ; i ++)
for(int j = 1 ; j <= n ; j ++)
{
if(g[i][j] == '*' && !cnt)
a = i, b = j, cnt ++;
else if(g[i][j] == '*') c = i, d = j;
}
if(a == c || b == d)
{
int sub = 1;
if(a == c)
{
if(a + sub <= n)
g[a + sub][b] = g[c + sub][d] = '*';
else
g[a - sub][b] = g[c - sub][d] = '*';
}
else // b == d
{
if(b + sub <= n)
g[a][b + sub] = g[c][d + sub] = '*';
else
g[a][b - sub] = g[c][d - sub] = '*';
}
}
else
{
g[a][d] = '*';
g[c][b] = '*';
}
for(int i = 1 ; i <= n ; i ++)
{
for(int j = 1 ; j <= n ; j ++)
cout << g[i][j];
cout << endl;
}
}
return 0;
}
C题
题意:给你一个01字符串,其中包含'?','?'可以转化为0或者1,现在问你能不能将所给字符串转换成恰好含有a个0和b个1的字符串
思路:首先直接贪心地把确定位置给确定了,比如当前位置是'1',那么对称位置一定得是'1',如果是'?'直接变'1',否则无解。
然后看能不能将剩下的'?'转换成想要的0和1即可,具体看如下代码。
代码如下
int a, b;
char s[N];
int main()
{
IOS;
int T;
cin >> T;
while(T --)
{
cin >> a >> b;
cin >> s + 1;
int len = strlen(s + 1);
int cnt = 0;
bool flag = false; //是否是奇数位中心是问号
bool res = true;
for(int i = 1 ; i <= (a + b + 1) / 2 ; i ++)
{
int r = len - i + 1;
if(s[i] != '?')
{
if(s[r] != '?')
{
if(s[i] != s[r])
{
res = false;
break;
}
}
else
s[r] = s[i];
}
else if(s[r] != '?')
{
if(s[i] != '?')
{
if(s[i] != s[r])
{
res = false;
break;
}
}
else
s[i] = s[r];
}
else
{
if(((a + b) & 1) && i == r) cnt ++;
else cnt += 2;
}
}
int cnt0 = 0, cnt1 = 0;
for(int i = 1 ; i <= a + b ; i ++)
{
if(s[i] == '0') cnt0 ++;
else if(s[i] == '1') cnt1 ++;
}
if(cnt0 > a || cnt1 > b) res = false;
if(res)
{
cnt0 = a - cnt0;
cnt1 = b - cnt1;
for(int i = 1 ; i <= (a + b + 1) / 2 ; i ++)
{
if(s[i] == '?')
{
int r = len - i + 1;
if(i == r)
{
if(cnt0) s[i] = '0', cnt0 --;
else if(cnt1) s[i] = '1', cnt1 --;
}
else if(cnt0 > 1)
{
s[i] = s[r] = '0';
cnt0 -= 2;
}
else if(cnt1 > 1)
{
s[i] = s[r] = '1';
cnt1 -= 2;
}
}
}
}
if(cnt0 || cnt1) res = false;
if(!res) cout << -1 << endl;
else cout << s + 1 << endl;
}
return 0;
}
D题
题意:给你一个长度为\(n + 2\)的数组,现在让你从中选择\(n\)个数字,使得剩下两个的其中一个是所选择的所有数字之和。
思路:显然不能选择最大的一个数字,直接排序,然后\(O(n)\)枚举。
代码如下
int n;
ll a[N];
ll s[N];
int main()
{
IOS;
int T;
cin >> T;
while(T --)
{
cin >> n;
for(int i = 1 ; i <= n + 2 ; i ++)
cin >> a[i];
sort(a + 1, a + n + 3);
for(int i = 1 ; i <= n + 1 ; i ++)
s[i] = a[i] + s[i - 1];
int d = 0;
for(int i = 1 ; i <= n + 1 ; i ++)
{
if(s[n + 1] - a[i] == a[i] || s[n + 1] - a[i] == a[n + 2])
{
d = i;
break;
}
}
if(!d) cout << -1 << endl;
else
{
for(int i = 1 ; i <= n + 1 ; i ++)
{
if(i == d) continue;
cout << a[i] << " ";
}
cout << endl;
}
}
return 0;
}
E题
题意:有一个长度为n的排列,现在问你能不能找到这个排列的一个形式A,使得它的\(\sum_{i = l}^rA[i] = s\)。
思路:显然在确定的区间长度下,能凑出来的数有一个最大值和最小值,如果在这个区间内,显然是有解的。
有解情况下的构造方式见代码。
代码如下
int n;
int s[N];
bool st[N];
int main()
{
IOS;
int T;
cin >> T;
while(T --)
{
int n, l, r, s;
cin >> n >> l >> r >> s;
int mind = 0, maxd = 0;
for(int i = 1 ; i <= r - l + 1 ; i ++)
mind += i;
for(int i = n ; i >= n - (r - l) ; i --)
maxd += i;
int len = r - l + 1;
if(s >= mind && s <= maxd)
{
int c = (s - mind) / len;
int left = (s - mind) % len;
int d = 0;
for(int i = len ; i >= 1 ; i --)
if(i + c + left <= n)
{
st[i + c + left] = true;
d = i;
break;
}
for(int i = 1 ; i <= len ; i ++)
{
if(i == d) continue;
st[i + c] = true;
}
int cnt = 0, now;
for(now = 1 ; now <= n ; now ++)
{
if(cnt + 1 == l) break;
if(!st[now])
cout << now << " ", cnt ++;
}
for(int i = 1 ; i <= n ; i ++)
if(st[i])
cout << i << " ";
for(; now <= n ; now ++)
{
if(!st[now])
cout << now << " ";
}
cout << endl;
memset(st, false, sizeof st);
}
else cout << -1 << endl;
}
return 0;
}
F题
题意:有n个位置,在每个位置每天有一个收益a[i],位置越靠近后面收益越高。同时每个位置有一个到达该位置所需要的花费,从第2个位置开始,到达该位置需要花费一定的费用b[i]。某人最开始在位置1,拥有的金钱为0,它可以待在当前位置,同时也可以通过一定的花费到达下一个位置,但只能一级一级到达。他想获得收益c的所需要的最少的天数。注意升级到下一个位置需要花费一天时间,并且不获得收益。
思路:首先明确一点,如果最优解是需要升级的,那么一定是在可以升级的时候就立马升级为最优,因此直接枚举最快到达各个等级下获得c的收益的天数,取最小值即可。
代码如下
int n, c;
ll a[N], b[N];
int main()
{
IOS;
int T;
cin >> T;
while(T --)
{
cin >> n >> c;
for(int i = 1 ; i <= n ; i ++) cin >> a[i];
for(int i = 1 ; i < n ; i ++) cin >> b[i];
ll res = 2e9;
ll now = 0, have = 0; //当前已经度过的天数和已经拥有的余额
for(int i = 1 ; i <= n ; i ++)
{
//在当前等级达成目的需要花费的天数
res = min(res, (c - have + a[i] - 1) / a[i] + now);
if(i < n)
{
//升级到下一级还需要工作多少天才够钱升级
int d = (b[i] - have + a[i] - 1) / a[i];
have += d * a[i] - b[i];
now += d + 1;
}
}
cout << res << endl;
}
return 0;
}
G题
这题建议直接看我的另一个题解:AcWing 1296. 聪明的燕姿
题意:让你找到一个最小正整数,使得它的所有约数之和为给定值\(c\).
思路:见上述另一个题解。
代码如下
int primes[N], cnt;
bool st[N];
int ans[N], len;
void get(int n)
{
for(int i = 2 ; i <= n ; i ++)
{
if(!st[i]) primes[cnt ++] = i;
for(int j = 0 ; primes[j] * i <= n ; j ++)
{
st[primes[j] * i] = true;
if(i % primes[j] == 0) break;
}
}
}
bool judge(int x)
{
if(x < N) return !st[x];
for(int i = 2 ; i <= x / i ; i ++)
if(x % i == 0) return false;
return true;
}
void dfs(int last, int sum, int s)
{
if(s == 1)
{
ans[len ++] = sum;
return;
}
if(s - 1 > (last >= 0 ? primes[last] : 1) && judge(s - 1)) ans[len ++] = sum * (s - 1);
for(int i = last + 1 ; primes[i] <= s / primes[i] ; i ++)
{
int p = primes[i];
for(int j = 1 + p, t = p ; j <= s ; t *= p, j += t)
if(s % j == 0)
dfs(i, sum * t, s / j);
}
}
int main()
{
IOS;
int T;
get(N - 1);
cin >> T;
while(T --)
{
int x;
cin >> x;
len = 0;
dfs(-1, 1, x);
sort(ans, ans + len);
if(!len) cout << -1 << endl;
else cout << ans[0] << endl;
}
return 0;
}