Educational Codeforces Round 61 (Rated for Div. 2) 题解

A: Regular Bracket Sequence

        判断括号是否匹配,只需要注意到 “()”这样的括号数量无所谓,每一个“((” 一定需要一个 “))” 来和它配对,每一个“)(”,只要有一对 “((” 和 “))” 就可以有任意多个 因为可以做成 “((” “)(””))“这样的形式。那么几个判断即可

代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> Pi;
typedef unsigned long long ULL;
int gcd(int a,int b){if (b == 0) return a; return gcd(b , a%b);}
int lcm(int a, int b){ return a/gcd(a,b)*b;}
inline int read(){
    int f = 1, x = 0;char ch = getchar();
    while (ch > '9' || ch < '0'){if (ch == '-')f = -f;ch = getchar();}
    while (ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}
const int maxn = 1e5 + 10;
int main(){
    int cnt1 = read(),cnt2 = read(),cnt3 = read(),cnt4 = read();
    if (cnt1 == cnt4){
        if (cnt3 > 0 && cnt1 == 0) cout << 0 << endl;
        else cout << 1 << endl;
    }else{
        cout << 0 << endl;
    }
    return 0;
}

B: Discounts

        对于每个q,我们自然想到贪心,去选取最贵的q个东西,然后最贵里面最便宜的那个的价格,就是我们可以便宜的最多的钱

排序,求和,对于每一个q,答案就是总和减去a[n-q+1]

 代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> Pi;
typedef unsigned long long ULL;
int gcd(int a,int b){if (b == 0) return a; return gcd(b , a%b);}
int lcm(int a, int b){ return a/gcd(a,b)*b;}
inline int read(){
    int f = 1, x = 0;char ch = getchar();
    while (ch > '9' || ch < '0'){if (ch == '-')f = -f;ch = getchar();}
    while (ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}
const int maxn = 3e5 + 10;
int a[maxn];
int q[maxn];
LL sum[maxn];
int main(){
   // freopen("/Users/chutong/data.txt", "r", stdin);
    int n = read();
    for (int i=1; i<=n; i++) {
        a[i] = read();
    }
    sort(a+1, a+n+1);
    for (int i=1; i<=n; i++) {
        sum[i] = sum[i-1] + a[i];
    }
    int m = read();
    for (int i=1; i<=m; i++) {
        q[i] = read();
        printf("%lld\n",sum[n]-a[n-q[i]+1]);
    }
    return 0;
}

 

C:Painting the Fence

        这是一个很有意思的题,有两种做法,首先讲暴力

        我们把每一个给定的[ l , r ] 区间染色,每给一个[l , r],这个区间上的点color[i]++

        然后暴力枚举,我当前删掉哪一个区间[ l , r ],首先给区间上的染色擦去 color[i]-- (l <= i <= r) 

        擦去吼然后最关键的一步,用一个sum数组,sum[i]表示 [1, i] 区间上,有多少个位置 color[i] == 1 ,注意是等于1,同时用                cnt统计一共有多少个位置被染色了 color[i] > 0

        之后再在第一层循环里,枚举第二个删掉哪个区间[ l1 , r1], 这时候,我们也希望对这个区间上的染色擦去,但是如果还是循环color[i]--,一定会TLE。我们就能看出sum数组的作用,sum[ri] - sum[li] 就代表,这段区间的染色如果被擦去,那么会有多少个点失去颜色。那么当我们枚举第二个区间的时候,并不需要真的擦去,cnt - (sum[r1] - sum[l1-1]) 就是擦去两段区间后,剩下的还有颜色的点,记录cnt 的最大值,即为答案,复杂度O(n*q + n^2)

代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> Pi;
typedef unsigned long long ULL;
int gcd(int a,int b){if (b == 0) return a; return gcd(b , a%b);}
int lcm(int a, int b){ return a/gcd(a,b)*b;}
inline int read(){
    int f = 1, x = 0;char ch = getchar();
    while (ch > '9' || ch < '0'){if (ch == '-')f = -f;ch = getchar();}
    while (ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}
const int maxn = 3e5 + 10;
int l[maxn],r[maxn],color[maxn];
int sum[maxn];
int main(){
 //   freopen("/Users/chutong/data.txt", "r", stdin);
    int n = read(),q = read();
    for (int i=1; i<=q; i++) {
        l[i] = read();
        r[i] = read();
        for (int j=l[i]; j<=r[i]; j++) {
            color[j]++;
        }
    }
    int ans = 0;
    for (int i=1; i<=q; i++) {
        for (int j=l[i]; j<=r[i]; j++) {
            color[j]--;
        }
        int tot = 0;
        for (int j=1; j<=n; j++) {
            sum[j] = sum[j-1];
            if (color[j] > 0){
                tot++;
            }
            if (color[j] == 1){
                sum[j] += 1;
            }
        }
    //    cout << tot << endl;
        for (int j=1; j<=q; j++) {
            if (i == j) continue;
            int now = tot - sum[r[j]] + sum[l[j]-1];
            ans = max(ans,now);
        }
        for (int j=l[i]; j<=r[i]; j++) {
            color[j]++;
        }
    }
    cout << ans << endl;
    return 0;
}

       

        第二种做法就是dp了,但是感觉不是很好想,对于每一个点 i ,有很多个区间覆盖在它上面,我们记录这些区间左端点的最小值为 l[i] ,那么用 dp[i] 代表区间[ 1,  i ] 中可以达到的最大染色数,就可以得到如下方程 

                                                 dp[i] = max ( dp[ l[i]-1 ] + i - l[i] + 1 , dp[i] ) 

                                                 dp[i] = max(dp[i],dp[i-1]);

       对于 i 位置来说,要得到最大的染色数,就是从最左端的位置 dp[l[i]-1] ,在加上,选取覆盖了当前位置的区间增加的染色数 i - l[i] + 1, 和这个位置本身的值取最大。或者,dp[i] 从i-1位置转移过来,去最大即可

代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> Pi;
typedef unsigned long long ULL;
int gcd(int a,int b){if (b == 0) return a; return gcd(b , a%b);}
int lcm(int a, int b){ return a/gcd(a,b)*b;}
inline int read(){
    int f = 1, x = 0;char ch = getchar();
    while (ch > '9' || ch < '0'){if (ch == '-')f = -f;ch = getchar();}
    while (ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}
const int maxn = 3e5 + 10;
int dp[maxn],l[maxn];
int main(){
   // freopen("/Users/chutong/data.txt", "r", stdin);
    int n = read(),q = read(),ll,rr;
    for (int i=1; i<=n; i++) {
        l[i] = INT_MAX;
    }
    for (int i=1; i<=q; i++) {
        ll = read(); rr = read();
        for (int j=ll; j<=rr; j++) {
            l[j] = min(l[j],ll);
        }
    }
    for (int k=0; k<q-2; k++) {
        for (int i=n; i>=1; i--) {
            if (l[i] == INT_MAX) continue;
            dp[i] = max(dp[l[i]-1]+i-l[i]+1,dp[i]);
        }
        for (int i=1; i<=n; i++) {
            dp[i] = max(dp[i],dp[i-1]);
        }
    }
    printf("%d\n",dp[n]);
    return 0;
}

 

D:Stressful Training

           我的作法为O(q*log^2(n)) 的,惊险卡过2972ms(时限3s),优化一下应该可以在2000ms左右(懒),但是应该有更好的做法可以优化掉一个log(n),有很多200ms左右跑过去的,留个坑学习一下。

           对于每一个 x 很容易想到二分,这个值满足二分条件。问题的关键在于如何检查当前的 x 是否符合条件,总是觉得每一个需要充电的值都没有一点确定的规律。

            所以选择了模拟,可以手写小顶堆,或者优先队列(手写快点),便利一遍时间轴1到k,每一个时间 t 出队一个人,这个人是所有人里面,电最先用完的,我们就先给他冲 1s 的电,然后重新计算用完电的时间,然后塞回队列里面。 

           如果,有一个人他在出队之后,发现他电用完电时间,比我们当前时间轴上的时间小,就说明我们尽可能的充电,也没办法让他在这个时间有电,因此这样的 x 是不满足条件的,但是如果遍历完时间轴,发现没有这样的点,就说明当前的x,可以完成供电任务。

代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL, int> pi;
const int maxn = 3e5 + 10;
int n,k;
LL a[maxn],b[maxn],c[maxn];
struct node {
    LL tim,id,tot;
    bool operator < (const node & a) const{
        return this->tim == a.tim ? this->id > a.id : this->tim > a.tim;
    }
};
bool check(LL x){
    priority_queue<node>pq;
    for (int i=1; i<=n; i++)
        pq.push((node){a[i]/b[i], i,a[i]});
    for (int i=0; i<k; i++) {
        node now = pq.top(); pq.pop();
       // cout << now.tim << ' ' << now.tot << ' ' << now.id << endl;
        if (now.tim < i) return false;
        if (now.tim >= k) break;
        now.tot += x;
        now.tim = now.tot/b[now.id];
        pq.push(now);
    }
    return true;
}
int main(){
   // freopen("/Users/chutong/data.txt", "r", stdin);
    scanf("%d%d",&n,&k);
    for(int i=1; i<=n; i++)
        scanf("%lld",&a[i]);
    for(int i=1; i<=n; i++)
        scanf("%lld",&b[i]);
    LL l = 0,r = 1e13;
    //if (check(5)) cout << "good" << endl;
    while (l != r) {
        LL mid = (l + r) >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    if (l == 1e13) cout << -1 << endl;
    else cout << l << endl;
    return 0;
}

 

F:Clear the String

      区间dp,dp[i][j] 代表 i 到 j 区间上删除字符串的最小值,每次都从最左边开始删,每删一次+1,删掉一个之后,剩下的串里面,找到一个和当前字符相同的字符在位置k,那么当前位置dp[i][j]最小值 =     min(dp[i][j], dp[i][k-1] + dp[k][j]); 

代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> Pi;
typedef unsigned long long ULL;
int gcd(int a,int b){if (b == 0) return a; return gcd(b , a%b);}
int lcm(int a, int b){ return a/gcd(a,b)*b;}
inline int read(){
    int f = 1, x = 0;char ch = getchar();
    while (ch > '9' || ch < '0'){if (ch == '-')f = -f;ch = getchar();}
    while (ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}
const int maxn = 1e3 + 10;
char s[maxn];
int n;
int dp[maxn][maxn];
int dfs(int l,int r){
    if (l > r) return 0;
    if (dp[l][r] != -1) return dp[l][r];
    int now = 1 + dfs(l+1, r);
    for (int i=l+1; i<=r; i++)
        if (s[i-1] == s[l-1])
            now = min(now, dfs(l+1, i-1) + dfs(i, r));
    dp[l][r] = now;
    return dp[l][r];
}
int main(){
   // freopen("/Users/chutong/data.txt", "r", stdin);
    scanf("%d%s",&n,s);
    memset(dp, -1, sizeof(dp));
    printf("%d\n",dfs(1, n));
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值