Loading

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;
}
posted @ 2020-11-28 19:52  dyhaohaoxuexi  阅读(68)  评论(0编辑  收藏  举报