Codeforces Round #695 (Div. 2)
A. Wizard of Orz
大意:
有n个时钟,都从0开始计时,每秒都会集体+1,如果当前是9,那么下一秒会变成0
现在可以在任意时间选择一个位置,使这个位置上的时钟停下来,然后过一秒钟后,相邻两个时钟会停下来,再过一秒钟后,再向外的两个时钟会停下来.....
最后全部的时钟停下来后,所有的数字可以组成一个长度为n的数,问这个数最大是多少
思路:
为了使数字最大,那么肯定是希望第一个位置在9停下,然后第二个位置相应的在8停下,如果一开始选择的是第二个位置,那么可以使第三位为9,此时数字最大
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int t, n;
int main() {
cin >> t;
while (t--) {
cin >> n;
if (n == 1)
cout << 9;
else if (n == 2)
cout << 98;
else if (n == 3)
cout << 989;
else {
cout << 989;
for (int i = 0; i < n - 3; i++) {
cout << i % 10;
}
}
cout << endl;
}
return 0;
}
B. Hills And Valleys
大意:
给出n个数,现在定义若\(a[i-1]>a[i]\)且\(a[i]<a[i+1]\)或者\(a[i-1]<a[i]\)且\(a[i]>a[i+1]\),则数列的威胁值+1
现在可以任选一个数,将其变为任意一个数,从而得到数列的一个新的威胁值,问这个威胁值最少可以是多少
思路:
因为必须是严格上升或者下降才能+1,所以如果将\(a[i]\)改为\(a[i-1]\)或者\(a[i+1]\),就会使得分数变化,而修改一个位置上的数,最多会影响三个位置的取值,所以只需要枚举一下修改的位置即可
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 5;
typedef long long LL;
int t, n;
int a[N];
int judge(int pos) {
if (pos == 0 || pos == n - 1) return 0;
if ((a[pos - 1] > a[pos]) && (a[pos + 1] > a[pos])) return 1;
if ((a[pos - 1] < a[pos]) && (a[pos + 1] < a[pos])) return 1;
return 0;
}
int main() {
cin >> t;
while (t--) {
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
int sum = 0;
int mx = 0;
for (int i = 0; i < n; i++) {
sum += judge(i);
if (i != 0 && i != n - 1) {
int tmp = 0;
int ori = 0;
tmp = judge(i - 1) + judge(i) + judge(i + 1);
int pre = a[i];
ori = tmp;
a[i] = a[i - 1];
tmp = min(tmp,judge(i - 1) + judge(i) + judge(i + 1));
a[i] = a[i + 1];
tmp = min(tmp,judge(i - 1) + judge(i) + judge(i + 1));
mx = max(mx, ori - tmp);
a[i] = pre;
}
}
cout << sum - mx << endl;
}
return 0;
}
C. Three Bags
大意:
给出三个背包,每个背包里有几个数字
每次操作可以从两个不同的背包里面各取出一个数字a和b,然后令a=a-b,然后把a放回去,b丢弃
问最后只剩下一个数时,这个数最大为多少
思路:
假设背包的编号为abc
两种情况:
一种是先将b1到bn全挂到a1上,然后把c2到cn全挂到a1上,最后把a1到an全挂到c1上,这样就是a数组全部取负,b和c数组全部为正
一种是留出b和c中最小的那个数取负,其他都取正即可
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 5;
typedef long long LL;
int n[3];
LL a[3][N], minx[3], sum[3];
int main() {
for (int i = 0; i < 3; i++) cin >> n[i];
for (int i = 0; i < 3; i++) {
minx[i] = 0x3f3f3f3f;
sum[i] = 0;
for (int j = 0; j < n[i]; j++) {
cin >> a[i][j];
minx[i] = min(minx[i], a[i][j]);
sum[i] += a[i][j];
}
}
LL res = -0x3f3f3f3f;
for (int i = 0; i < 3; i++) {
LL tmp = 0;
for (int j = 0; j < 3; j++) {
if (i == j)
tmp -= sum[j];
else
tmp += sum[j];
}
res = max(res, tmp);
//cout << tmp << endl;
}
for (int i = 0; i < 3; i++) {
LL tmp = 0;
for (int j = 0; j < 3; j++) {
if (i == j)
tmp += sum[j];
else
tmp += sum[j]-2*minx[j];
}
res = max(res, tmp);
//cout << tmp << endl;
}
cout << res << endl;
return 0;
}
D. Sum of Paths
大意:
一个长度为n的数轴,一个机器人在从任意一个位置开始,走k步,每步都可以向左或向右走1步。
走k步之后会得到一条轨迹,轨迹的权值就是每个点的权值和,那么对于这个长度为n的数轴,就可以求出所有轨迹的权值和。
现在给出m次修改,将x位置上的权值改为y,输出每一次修改后的所有轨迹的权值和
思路:
首先\(dp[i][j]\)代表在第j步走到第i位置的方案数,转移方程为:
\(dp[i][j]=dp[i+1][j-1]+dp[i-1][j-1]\)
然后计算\(c[i]\)代表第i个位置上的数贡献了多少次:
$c[j] += dp[j][i] * dp[j][k - i] $
此时\(dp[j][k - i]\)代表从j这里走出去k-i次的方案数,因为和别的地方走k-i次到j处的方案数相同,所以可以直接用
最后就是每次计算权值和即可
#include <bits/stdc++.h>
using namespace std;
const int N = 5e3 + 5;
typedef long long LL;
int n, k, m;
const int mod = 1e9 + 7;
LL a[N], dp[N][N], c[N], sum = 0;
int main() {
cin >> n >> k >> m;
for (int i = 1; i <= n; i++) dp[i][0] = 1;
for (int i = 1; i <= k; i++) {
for (int j = 1; j <= n; j++) {
if (j == 1)
dp[j][i] = dp[j + 1][i - 1];
else if (j == n)
dp[j][i] = dp[j - 1][i - 1];
else
dp[j][i] = (dp[j - 1][i - 1] + dp[j + 1][i - 1])%mod;
}
}
for (int i = 0; i <= k; i++) {
for (int j = 1; j <= n; j++) {
c[j] += dp[j][i] * dp[j][k - i] % mod;
c[j] %= mod;
}
}
for (int i = 1; i <= n; i++) {
cin >> a[i];
sum += a[i] * c[i] % mod;
sum %= mod;
}
while(m--){
int x,y;
cin>>x>>y;
sum -= a[x] * c[x] % mod;
sum = (sum + mod) % mod;
a[x] = y;
sum += a[x] * c[x] % mod;
sum = (sum + mod) % mod;
cout << sum << endl;
} return 0;
}
E. Distinctive Roots in a Tree
大意:
定义一个点为distinctive roots,如果从从这个点到树上其他点的路径中,所有的权值只出现过一次。
求出distinctive roots的个数
思路:
直接求哪个点是distinctive roots没有思路,可以考虑删去全部不符合条件的点,剩下的都是distinctive roots
先随便找一个点开始dfs,然后对于每个点,如果子树中存在和这个点的权值相同的点,那么这个点的其他子树都不能符合条件,而且这个点之上的所有点都不符合条件。
如果这个和这个点权值相同的点不全在子树上,那么这个点的所有子树都不符合条件
这两种情况通过树上差分标记一下即可,需要注意的是第一种情况难以直接全标记出来,可以结合第二种情况进行标记
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
typedef long long LL;
int n, w[N], c[N], xu[N], cnt = 0,num,have[N],ed[N],total[N];
vector<int> mp[N];
map<int, int> Hash;
void dfs(int now, int fa) {
int hashv = Hash[w[now]];
xu[now] = ++cnt;
int prehave = have[hashv];
have[hashv]++;
for (int i = 0; i < mp[now].size(); i++) {
int ne = mp[now][i];
if (ne == fa) continue;
int pre = have[hashv];
dfs(ne, now);
if(pre<have[hashv]){
c[1]++;
c[xu[ne]]--;
c[ed[ne] + 1]++;
}
}
ed[now] = cnt;
if(have[hashv]-prehave<total[hashv]){
c[xu[now]]++;
c[ed[now] + 1]--;
}
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> w[i];
if (Hash[w[i]] == 0) Hash[w[i]] = ++num;
total[Hash[w[i]]]++;
}
for (int i = 1; i < n; i++) {
int x, y;
cin >> x >> y;
mp[x].push_back(y);
mp[y].push_back(x);
}
dfs(1, 0);
int res = 0;
for (int i = 1; i <= n;i++){
c[i] += c[i - 1];
if (c[i] == 0) res++;
}
cout << res << endl;
return 0;
}