昨天打了一下CF695div2的比赛,比赛中就做出了A,B题,A题7分钟大约,但是B题因为一个细节卡了一个多小时,C,D,E今天补了一下,感觉D题确实不难。
题目链接:https://codeforces.com/contest/1467
A. Wizard of Orz
https://codeforces.com/contest/1467/problem/A
题意:一个n位长的整数,之前全是0,每一秒+1,9之后1秒变成0,相当于每秒+1并对10取模。你可以在任何一秒钟,在一个任意的位置,让序列暂停。它的相邻位过1秒钟暂停,相隔为2的位置过2秒钟暂停,以此类推。比如9999,我们让此时第三位停下,过一秒变成0090,再过一秒变成1090,这样就完全停下。问你,如此操作后得到的最大的数是多少?
思路:你肯定要让第一位是9,这样才有最优解,同时第二位不能是9,但是可以取8。我们再考虑第三位,这一位可以取9。我们可以让序列在8888888~~的第二个8处暂停,1秒后变成98999999。这样就构造出了最优解。
因此,最优解的第一位是9,第二位是8,第三位是9,第四位是0,第五位是1,之后就是+1对10取模了。
#include<bits/stdc++.h>
using namespace std;
int _;
int n;
int ans[200010];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> _;
while(_--){
cin >> n;
for(int i = 1; i <= n+2; i++){
ans[i] = 0;
}
ans[1] = 9;
ans[2] = 8;
for(int i = 3; i <= n; i++){
ans[i] = (ans[i-1] + 1)%10;
}
for(int i = 1; i <= n; i++){
cout << ans[i];
}
cout << "\n";
}
return 0;
}
B. Hills And Valleys
https://codeforces.com/contest/1467/problem/B
题意:一个长度为n的序列, a 1 , a 2 , , , , , , , a n a_1,a_2,,,,,,,a_n a1,a2,,,,,,,an,如果 a i > a i + 1 & & a i > a i − 1 a_i > a_{i+1} \&\& a_i > a_{i-1} ai>ai+1&&ai>ai−1,第i个位置是波峰,如果 a i < a i + 1 & & a i < a i − 1 a_i < a_{i+1} \&\& a_i < a_{i-1} ai<ai+1&&ai<ai−1,第i个位置是波谷。你可以改一个序列中的数,让波峰和波谷的数量和达到最小值。
思路:我们可以分析得出,我们肯定要改一个波峰或者波谷来取得最优解,如果我们要改 a i a_i ai,对于它的修改,改成 a i − 1 a_{i-1} ai−1或者改成 a i + 1 a_{i+1} ai+1肯定是最优的。我们只需要枚举对每处修改对总答案的贡献,取一个最大的就好了。
细节:我最开始选择了一个贪心策略是不行的,就是简单的,判断波峰波谷波峰,波谷波峰波谷位置相邻总答案-3,波峰波谷相邻总答案-2,其余情况总答案不得0就减1。如果波峰波谷相邻,改一个波峰或波谷,可能会在之前或之后生成一个新的波峰或波谷。
你可以参考一下这组数据,发现问题:6 3 2 4 3 2
它的答案是1。
#include<bits/stdc++.h>
using namespace std;
int _;
int n;
int a[300010];
int h[300010];
int v[300010];
bool judge(int t){
if(a[t] > a[t-1] && a[t] > a[t+1])
return true;
if(a[t] < a[t-1] && a[t] < a[t+1])
return true;
return false;
}
int sum = 0;
int ans1, maxx, tmp;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> _;
while(_--){
cin >> n;
sum = 0;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
for(int i = 1; i <= n; i++){
h[i] = v[i] = 0;
}
for(int i = 2; i < n; i++){
if(a[i] > a[i-1] && a[i] > a[i+1]){
h[i] = 1;
sum += 1;
}
if(a[i] < a[i-1] && a[i] < a[i+1]){
v[i] = 1;
sum += 1;
}
}
ans1 = 0;
maxx = 0;
tmp = -1;
for(int i = 2; i < n; i++){
int now = a[i];
ans1 = 0;
a[i] = a[i-1];
if(h[i]){
ans1++;
if(v[i-1]){
ans1++;
}
if(i < n-1){
tmp = judge(i+1);
if(!v[i+1]&&!h[i+1]&&tmp){
ans1--;
}
if(v[i+1]&&!tmp){
ans1++;
}
}
maxx = max(maxx, ans1);
}
else if(v[i]){
ans1++;
if(h[i-1]){
ans1++;
}
if(i < n-1){
tmp = judge(i+1);
if(!v[i+1]&&!h[i+1]&&tmp){
ans1--;
}
if(h[i+1]&&!tmp){
ans1++;
}
}
maxx = max(maxx, ans1);
}
a[i] = now;
}
for(int i = 2; i < n; i++){
int now = a[i];
ans1 = 0;
a[i] = a[i+1];
if(h[i]){
ans1++;
if(v[i+1]){
ans1++;
}
if(i > 2){
tmp = judge(i-1);
if(!v[i-1]&&!h[i-1]&&tmp){
ans1--;
}
if(v[i-1]&&!tmp){
ans1++;
}
}
maxx = max(maxx, ans1);
}
else if(v[i]){
ans1++;
if(h[i+1]){
ans1++;
}
if(i > 2){
tmp = judge(i-1);
if(!v[i-1]&&!h[i-1]&&tmp){
ans1--;
}
if(h[i-1]&&!tmp){
ans1++;
}
}
maxx = max(maxx, ans1);
}
a[i] = now;
}
sum -= maxx;
cout << sum << "\n";
}
return 0;
}
C. Three Bags
https://codeforces.com/contest/1467/problem/C
题意:你有三个背包,可以每一次从两个非空背包中取出两个数。比如你从第一个背包中取出了a,第二个背包中取出了b。之后第一个背包中的a变成a-b,第二个背包中的b就没有了。多次如此操作,最后一个背包有一个数,其余两个背包是空的。问此时剩余的最大值是多少。
思路:贪心。结论是:你最后的总贡献是所有数的和值减去一个背包的所有数的值的2倍,或者两个不同背包的最小值之和的2倍。
分析过程:对于一个b操作奇数次,是负贡献,操作偶数次,是正贡献。一个数经过从A换到B,贡献为正,要经过一下C。最后就有可能出现这样的情况,A,B,C里面都只有一个数,且两个背包中的数相当于还没有做过交换,自然变成了负贡献(其实就是之前的交换都是用这两个不同背包的最小值做中介,可以手推一下)。同时,我们还有一种可能就是,A背包全部进入C,再把C全部进入B,只有A, B的贡献是正,C背包里面的所有数贡献都是负数。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n1, n2, n3;
ll a1[300010];
ll a2[300010];
ll a3[300010];
int main(){
cin >> n1 >> n2 >> n3;
ll sum[4] = {
0, 0, 0, 0};
ll m[4] = {
(ll)2e9, (ll)2e9, (ll)2e9, (ll)2e9};
for(int i = 1; i <= n1; i++){
cin >> a1[i];
sum[1] += a1[i];
m[1] = min(m[1], a1[i]);
}
for(int i = 1; i <= n2; i++){
cin >> a2[i];
sum[2] += a2[i];
m[2] = min(a2[i], m[2]);
}
for(int i = 1; i <= n3; i++){
cin >> a3[i];
sum[3] += a3[i];
m[3] = min(m[3], a3[i]);
}
sort(sum+1, sum+4);
sort(m+1, m+4);
ll ans = sum[1] + sum[2] + sum[3] - 2*min(m[1]+m[2], sum[1]);
cout << ans << "\n";
return 0;
}
D. Sum of Paths
https://codeforces.com/contest/1467/problem/D
题意:有个机器人在[1,n]的直线方格上面走k步,求所有路线下每个格子经过的次数。格子有权值,求路径的权值和。
思路:dp。设dp[i][j]表示经过i次到达j点的路径数。再设sum[i]表示所有路径经过i点的次数,到达i点和从i点触出发的路径是等效的,我们把i当中转点,枚举中转点前后到i的路径数目即可。每一点的权值乘上这一点的经过次数就是这一点对答案的总贡献。然后修改一个点的权值,答案就可以在O(1)内完成。
//对于每一个方格,枚举作为中转点的情况
//前缀和优化
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
ll sum[5020];
ll dp[5020][5020];//第i次走到j格的路线数。然后成a[i]
ll a[5020];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, k, q;
cin >> n >> k >> q;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
for(int i = 1; i <= n; i++){
dp[0][i] = 1;
}
for(int i = 1; i <= k; i++){
dp[i][1] = dp[i-1][2]%mod;
dp[i][n] = dp[i-1][n-1]%mod;
for(int j = 2; j <= n-1; j++){
dp[i][j] = (dp[i-1][j-1] + dp[i-1][j+1])%mod;
}
}
for(int i = 1; i <= n; i++){
for(int j = 0; j <= k; j++){
sum[i] = (sum[i] + dp[j][i]*dp[k-j][i]%mod)%mod;
}
}
ll ans = 0;
for(int i = 1; i <= n; i++){
ans = (ans + sum[i]*a[i]%mod)%mod;
}
while(q--){
int i;
ll x;
cin >> i >> x;
ans = ((ans - sum[i]*a[i]%mod + mod)%mod + sum[i]*x%mod)%mod;
cout << ans << "\n";
a[i] = x;
}
return 0;
}
E. Distinctive Roots in a Tree
https://codeforces.com/contest/1467/problem/E
题意:问你一棵树上的特色点有多少个。特色点:从一点出发到达其它节点的路径中,每一条单一路径中碰到的点的权值都是不同的。
思路:很明显和点的权值具体大小没有关系,可以用它的相对大小,我们先离散化一下。我们发现对于一个节点的权值会在它的子树之外出现,那么这一棵子树都是不行的。如果一个节点的权值,出现在它的子树A中,它的子树中除了A以外的子树是可行解,我们考虑用一下树的前缀和进行运算,其实用一个差分或者标记式延迟更新会更好。
#include<bits/stdc++.h>
//用树的前缀判定每一个点可不可以
//离散化,和数值大小没有具体关系
//数值相对排名代替数值
//dfs序
//差分
using namespace std;
int a[200010];
int b[200010];
int cnt[200010];//每一个数值一共出现了多少次
int dfn[200010];//dfs虚列
int c[200010];//差分数组
int nowsum[200010];//当前的值出现了几次
vector<int> Edge[200010];
int n;
int tot = 0;
int siz[200010];
void cover(int l, int r, int k){
//差分约束
if(l > r){
return;
}
c[l] += k;
c[r+1] -= k;
}
void dfs(int i, int fa){
dfn[i] = ++tot;
siz[i] = 1;
int temp = nowsum[a[i]];
++nowsum[a[i]];
for(auto v : Edge[i]){
if(v == fa){
continue;
}
int tmp = nowsum[a[i]];
dfs(v, i);
if(nowsum[a[i]] > tmp){
cover(1, n, 1);
cover(dfn[v], dfn[v]+siz[v]-1, -1);
}
siz[i] += siz[v];
}
if(nowsum[a[i]] - temp < cnt[a[i]]){
//其它子树有相同的值
cover(dfn[i], dfn[i]+siz[i]-1, 1);
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
b[i] = a[i];
}
sort(b+1, b+1+n);
int m = unique(b+1, b+1+n) - b - 1;
for(int i = 1; i <= n; i++){
a[i] = lower_bound(b+1, b+1+n, a[i]) - b;
cnt[a[i]]++;
}
for(int i = 1, u ,v; i <= n-1; i++){
cin >> u >> v;
Edge[u].push_back(v);
Edge[v].push_back(u);
}
dfs(1, 0);
int ans = 0;
for(int i = 1; i <= n; i++){
c[i] += c[i-1];
if(c[i] == 0){
ans++;
}
}
cout << ans << "\n";
return 0;
}