codeforces 695-div2 A-E题解

昨天打了一下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>ai1,第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<ai1,第i个位置是波谷。你可以改一个序列中的数,让波峰和波谷的数量和达到最小值。
思路:我们可以分析得出,我们肯定要改一个波峰或者波谷来取得最优解,如果我们要改 a i a_i ai,对于它的修改,改成 a i − 1 a_{i-1} ai1或者改成 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;
}

猜你喜欢

转载自blog.csdn.net/Einsteinme/article/details/112412388