• Educational Codeforces Round 98 (Rated for Div. 2)


    A. Robot Program

    题意:

    一个机器人要从((0,0))位置走到((x,y))位置,每次可以进行5种操作,分别是向上下左右方向走一格,或者是待在原地。机器人不能连续进行同样的两次操作(例如不能连续向右走两次,但是可以走一次,停在原地一次,然后再走一次),问最少进行多少次操作可以到达目的地

    思路:

    (x=y)或者x与y的差只有1时,可以一直走,不需要停,否则必须要在中途停下来。

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e6 + 5;
    int t, x, y;
    int main(){
        cin>>t;
        while(t--){
            cin >> x >> y;
            if(abs(x-y)<=1){
                cout << x + y << endl;
            }
            else{
                cout << min(x, y) * 2 + 1 + 2 * (max(x, y) - (min(x, y) + 1)) << endl;
            }
        }
        return 0;
    }
    

    B. Toy Blocks

    题意:

    有 n 个盒子,每个盒子有 (a_i) 个糖果。现在你可以向一些盒子中添加糖果,使得任选一个盒子,将其中的糖果分到其余 n-1 个盒子中,每个盒子的糖果数量相等。问最少需要添加多少糖。

    思路

    直接暴力做,对于每个点,判断一下把其他糖果填平的糖果即可,这里的填平是需要将其他的糖果补充到他们的最大值,然后再看剩下的糖果数量,判断是否还需要再填一层

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e5 + 5;
    int t, n, x, a[N];
    long long anss;
    long long sum,maxn=-1,minn=0x3f3f3f3f;
    
    int main(){
        cin >> t;
        while(t--){
            cin >> n;
            sum = 0;
            anss = 0;
            for (int i = 0; i < n;i++){
                cin >>a[i];
                sum += a[i];
            }
            sort(a, a + n);
            maxn = a[n - 1];
            minn = a[0];
            for (int i = 0; i < n;i++){
                minn = a[i];
                if(i==n-1){
                    maxn = a[i - 1];
                }
                if (maxn * (n - 1) - (sum - minn) > minn)
                {
                   anss=max(anss, maxn * (n - 1) - (sum - minn) - minn );
                }
                else if (maxn * (n - 1) - (sum - minn) > minn)
                {
                    anss = max(anss, (long long)0);
                }
                else{
                if(((minn-(maxn*(n-1)-(sum-minn)))%(n-1)==0)){
                    anss = max(anss, (long long)0);
                }
                else{
                    anss = max(anss, (n - 1) - (minn - (maxn * (n - 1) - (sum - minn))) % (n - 1));
                }
            }
                
            }
            cout << anss << endl;
        }
        return 0;
    }
    

    C. Two Brackets

    题意:

    计算有多少配对的括号

    思路:

    记录一下前面出现的左括号lsum,当遇到右括号时,如果lsum大于0,就将lsum--,然后答案++

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e6 + 5;
    int t,lnum1,lnum2,sum;
    string s;
    int main(){
        cin>>t;
        while(t--){
            cin >> s;
            sum = 0;
            lnum1 = 0;
            lnum2 = 0;
            for (int i = 0;i<s.size();i++){
                if(s[i]=='('){
                    lnum1++;
                }
                else if(s[i]==')'){
                    if(lnum1){
                        lnum1--;
                        sum++;
                    }
                }
                else if(s[i]=='['){
                    lnum2++;
                }
                else if(s[i]==']'){
                    if(lnum2){
                        lnum2--;
                        sum++;
                    }
                }
            }
            cout << sum << endl;
        }
        return 0;
    }
    

    D. Radio Towers

    题意:

    一共0到n+1共n+2个点,每个点上都可以放置或者不放置一个信号塔,信号塔的辐射范围是1-n,每个点只能被一个信号塔覆盖,0号和n+1号点不能被覆盖,问符合条件的放置方法占全部的多少

    思路:

    找规律即可,分子为斐波那契数列,分母为(2^n)

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e6 + 5;
    const int mod = 998244353;
    typedef long long LL;
    LL n,fz,fm,fib[N];
    LL get_fz(LL n){
        fib[1] = 1;
        fib[2] = 1;
        for (int i = 2; i <= n;i++){
            fib[i] = ((LL)fib[i - 1] + (LL)fib[i - 2]) % mod;
        }
        return fib[n];
    }
    
    LL qmi(LL a, LL k, LL p)   {
        LL res = 1 % p;  // res记录答案, 模上p是为了防止k为0,p为1的特殊情况
        while(k) { // 只要还有剩下位数
            if (k & 1) res = (LL)res * a % p;  // 判断最后一位是否为1,如果为1就乘上a,模上p, 乘法时可能爆int,所以变成long long
            k >>= 1;  // 右移一位
            a = (LL) a * a % p;  // 当前a等于上一次的a平方,取模,平方时可能爆int,所以变成long long
        }
        return res;
    }
    
    LL get_inv(LL a, LL p) {
        return a % p == 0? -1: qmi(a, p - 2, p);
    }
    
    LL get_fm(LL n){
        LL temp = qmi(2,n,mod);
        return get_inv(temp, mod);
    }
    
    int main(){
        cin>>n;
        fz = get_fz(n);
        fm = get_fm(n);
        cout << fz * fm % mod << endl;
        return 0;
    }
    

    E. Two Editorials

    大意:

    一共有m个位于[1,n]的区间p,现在有长度为K的区间b和c。设对于区间p[i],定义a[i]为p[i]分别与b,c相交长度的较大值,现在问区间b和c位于何处时,a[i]的和最大

    思路:

    纯暴力肯定不行,所以需要考虑对其进行优化,因为对于区间p,如果它的中点在b和c的中点的左侧,那么一定是对b产生贡献,反之则是对c产生贡献,那么对于每个点i,p个区间都可以分为两部分,前一部分对b产生贡献,后一部分对c产生贡献,所以首先对m个p区间按照中点排序,然后遍历n,找到对于每个点i的分界点,即(pre[i])代表有多少区间的中点在i的左侧。然后跑一遍前缀和,(sum[i][j])代表对于位置i,前j个区间可以产生的贡献,最后暴力枚举区间b和c,算一下两个区间获得的贡献即可。这样复杂度为(O(n*m))

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 2e3 + 5;
    int n, m, k,pre[N],sum[N][N];
    struct node
    {
        int l, r;
    }a[N];
    
    bool cmp(node a,node b){
        return (a.l + a.r < b.l + b.r);
    }
    
    int main(){
        cin>>n>>m>>k;
        for (int i = 0; i < m;i++){
            cin >> a[i].l >> a[i].r;    
        }
        sort(a, a + m, cmp);
        int j = 0;
        for (int i = 1; i <= n;i++){
            while(j<m&&(a[j].l+a[j].r<=2*i)){
                j++;
            }
            pre[i] = j-1;
        }
        for (int i = 1; i <= n - k + 1;i++){
            int l = i, r = i + k - 1;   //当前区间的左右范围
            for (int j = 1; j <= m;j++){
                sum[i][j] = sum[i][j - 1] + max(0, min(a[j - 1].r, r) - max(a[j - 1].l,l )+1);
            }
        }
        int res = 0;
        for (int i = 1; i <= n - k + 1;i++){
            for (int j = i; j <= n - k + 1;j++){
                int mid = (i + j + k - 1) >> 1;
                res = max(res, sum[i][pre[mid] + 1] + sum[j][m] - sum[j][pre[mid] + 1]);
            }
        }
        cout << res << endl;
        return 0;
    }
    

    F. Divide Powers

    题意:

    给出一个数组(cnt_i)代表当前有(cnt_i)(2^i),当(i>0)时,可以将一个(2^i)变成两个(2^{i-1}),现在又两个操作,一类是修改(cnt_i),另一类是询问进行多少次修改后会有不少于(k)个元素小于等于(2^x)

    思路:

    对于大于(2^x)的元素,将其修改到(2^x),可以比修改小于(2^x)的元素要多出一个,所以优先修改大于(2^x)的元素,但是如果修改到(2^x)获得的元素比需要的多,那么需要判断是否修改小于(2^x)的元素,也就是需要记录当前已经选择的元素,让他们全部修改为(2^0)可以获得的元素个数。

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e6 + 5;
    typedef long long LL;
    LL n, q, cnt[35],op,a[35];
    int main(){
        cin >> n >> q;
        a[0] = 1;
        for (int i = 0; i < n;i++){
            cin >> cnt[i];
            if(i){
                a[i] = a[i - 1] * 2;
            }
        }
        while(q--){
            cin >> op;
            if(op==1){
                LL pos, val;
                cin >> pos >> val;
                cnt[pos] = val;
            }
            else{
                LL x, k,sum=0,res=0;
                cin >> x >> k;
                for (int i = 0; i <= x;i++){
                    k -= cnt[i];
                    sum += (a[i]-1) * cnt[i];   //记录当前选择的还可以多产生多少符合条件的值
                }
                if(k<=0){
                    cout << 0 << endl;
                    continue;
                }
                for (int i = x + 1; i < n;i++){
                    if((a[i-x]*cnt[i])<=k){
                        k -= a[i - x] * cnt[i];
                        res += (a[i - x] - 1) * cnt[i];
                        sum += (a[i] - a[i - x]) * cnt[i];
                    }
                    else{
                        int p=k/a[i-x];
    					k%=a[i-x];
    					sum+=p*(a[i]-a[i-x]);
    					res+=p*(a[i-x]-1);
    					for (int j=i;j>=x;j--)
    					{
    						if (k<=sum)
    						{
    							res+=k;
    							k=0;
    							break;
    						} 
    						if (a[j-x-1]<=k)
    						{
    							k-=a[j-x-1];
    							sum+=a[j-1]-a[j-x-1];
    							res+=a[j-x-1];
    						}
    						else res++;
    					}
                        break;
                    }
                }
                if(k<=sum){
                    cout << res + k << endl;
                }
                else
                cout << -1 << endl;
            }
        }
            return 0;
    }
    

    G. Game On Tree

    大意:

    Alice 和 Bob 在玩一个游戏。他们有一棵由 n 个结点组成的树。一开始,Bob 有 k个卡片,其中第i个卡片位于结点 (a_i),在游戏开始之前,Alice 将在这棵树的一个结点上放置一个卡片。

    这个游戏由一些回合组成。每个回合都将有以下事件发生(完全按照以下顺序):

    1. Alice 可以把她的卡片移到相邻的结点,或者不移动;
    2. 对于 Bob 的每一张卡片,他可以把这张卡片移到相邻的结点,或者不移动。注意:每个卡片的选择都是独立的。

    当 Alice 的卡片与 Bob 的任意一张(或多张)卡片在同一结点时,游戏结束。(Bob 自己的多张卡片可以置于同一结点上,即使它们的初始位置一定是不同的)。

    Alice 希望游戏回合越多越好,Bob则相反。如果某回合中间游戏结束(即 Alice 把卡片移到了有 Bob 卡片的结点上),这回合依然算入总回合数。

    对于每个结点,计算 Alice 一开始将卡片放在该结点时游戏将持续的回合数。

    思路:

    首先利用拓扑排序求出来每个点距离Alice的最近的一个卡片的距离(dis[i]),对于Bob来说,必然是想前往(dis[x])较大的点,而对于Alice来说,必然是所有的点都向Bob逼近,得到(dis[x])数组后,可以从最大的值开始更新周围的点的答案,但是这样复杂度会很高,所以引入(h[i])数组,代表上一次利用i点更新周围点的答案时,i点距离最近的一个“敌人”的距离,所以只有当当前距离最近的"敌人"的距离大于(h[i])时,才利用这个点更新答案,否则不需要更新。

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 2e5 + 5;
    int n, m,res[N],dis[N],maxn,h[N];
    vector<int> mp[N],D[N];
    queue<int>q;
    
    int main(){
        cin >> n;
        for (int i = 1; i < n;i++){
            int u,v;
            cin >> u >> v;
            mp[u].push_back(v);
            mp[v].push_back(u);
            dis[i] = -1;
        }
        dis[n] = -1;
        cin >> m;
        while(m--){
            int x;
            cin >> x;
            dis[x] = 0;
            q.push(x);
        }
        while(!q.empty()){
            int u = q.front();
            q.pop();
            for (int i = 0; i < mp[u].size();i++){
                int v = mp[u][i];
                if(dis[v]>=0)
                    continue;
                dis[v] = dis[u] + 1;
                q.push(v);
            }
            D[dis[u]].push_back(u);
            maxn = max(maxn, dis[u]);
        }
        queue<pair<int, int>> q1;
        for (int i = maxn; i > 0;i--){
            for (int j = 0; j < D[i].size();j++){
                int u = D[i][j];
                if(h[u]<dis[u]){
                    if(res[u]==0){
                        res[u] = i;
                    }
                    h[u] = dis[u];
                    q1.push({dis[u],u});
                }
                while(!q1.empty()){
                    int d = q1.front().first;
                    int u = q1.front().second;
                    q1.pop();
                    if(--d==0)
                        continue;   //更新过来直接是敌人
                    for(int j = 0;j<mp[u].size();j++){
                        int v = mp[u][j];
                        int temp = min(d, dis[v]);
                        if(temp>h[v]){
                            if(res[v]==0)
                                res[v] = i;
                            h[v] = temp;
                            q1.push({temp, v});
                        }
                    }
                }
            }
        }
        for (int i = 1; i <= n;i++){
            cout << res[i] << ' ';
        }
        cout << endl;
        return 0;
    }
    
  • 相关阅读:
    Navicat For SQL Server 修改字段为自增主键
    navicat for sql server 12下载地址
    git 同时关联多个远程库
    Mysql general_log 日志详解
    angular教程
    Python代码写好了怎么运行?
    python mysql自增字段AUTO_INCREMENT值的修改方式
    Python自学教材推荐 初学者必看
    永久性差异
    如何关闭搜狗的流氓弹窗广告
  • 原文地址:https://www.cnblogs.com/dyhaohaoxuexi/p/14053787.html
Copyright © 2020-2023  润新知