【 Codeforces Educational Codeforces Round 61 】 A B C D E F G

71 篇文章 0 订阅

Codeforces Educational Codeforces Round 61   A B C D F

来补补题解啦

A . Regular Bracket Sequence

https://codeforces.com/contest/1132/problem/A

题意 给你四种括号的个数 问你能否组成合法括号数 

括号数1 ((

括号数2 ()

括号数3 )(

括号数4 ))

我们发现括号数2 一定符合,所以只有括号数3当一起连起来的时候 他的左边缺一个括号1 右边缺一个括号4 补了以后 剩下的括号1和括号4要匹配 所以我们得到 2无所谓 有3一定有1和4 1必须和4一样 AC

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <cmath>
#include <map>
#include <stack>
#include <set>
#include <sstream>
#include <vector>
#include <stdlib.h>
#include <algorithm>
using namespace std;
#define dbg(x) cout<<#x<<" = "<< (x)<< endl
#define dbg2(x1,x2) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<endl
#define dbg3(x1,x2,x3) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<" "<<#x3<<" = "<<x3<<endl

int main()
{
    int a,b,c,d;
    scanf("%d%d%d%d",&a,&b,&c,&d);
    if(a==0&&b==0&&c==0&&d==0)
    {
        printf("1\n");
        return 0;
    }
    if(a!=d||c&&!a||c&&!d)
    {
        printf("0\n");
        return 0;
    }
    printf("1\n");
    return 0;
}

B . Discounts

https://codeforces.com/contest/1132/problem/B

题意 给你n个商品 m 个折扣券 折扣券的使用条件为买qi个东西 你只需要付qi-1个最贵的商品的钱 那我们知道 如果使用折扣券一定是要让最贵的能免单 那么就是第q个免单 所以按从大到小排序 折扣券就是总和减去第qi大的商品的价格

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <cmath>
#include <map>
#include <stack>
#include <set>
#include <sstream>
#include <vector>
#include <stdlib.h>
#include <algorithm>
using namespace std;
#define dbg(x) cout<<#x<<" = "<< (x)<< endl
#define dbg2(x1,x2) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<endl
#define dbg3(x1,x2,x3) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<" "<<#x3<<" = "<<x3<<endl
const int MAX_N = 300025;
long long arr[MAX_N];
int q[MAX_N];
bool cmp(long long a,long long b)
{
    return a>b;
}
int main()
{
    int n;
    long long sum = 0;
    scanf("%d",&n);
    for(int i = 1;i<=n;++i) scanf("%lld",&arr[i]),sum+=arr[i];
    sort(arr+1,arr+1+n,cmp);
    int m ;
    scanf("%d",&m);
    for(int i = 1;i<=m;++i) scanf("%d",&q[i]);
    for(int i = 1;i<=m;++i)
    {
        printf("%lld\n",sum-arr[q[i]]);
    }
    return 0;
}

C . Painting the Fence

https://codeforces.com/contest/1132/problem/C

题意 给你$q$个线段 问你$q-2$个区间组合能形成的区间包含最大的数

做过一道牛客类似的取$q-1$个区间组合形成的 就是开一个前缀和记录该点只出现一次的有多少个

既然是q-2那么就是开第二个前缀和 该点出现了两次有多少个

然后n^2暴力枚举 i 和 j 答案就是所有的减去 sum[1][arr[i].r]-sum[1][arr[i].l-1] 和 sum[1][arr[j].r]-sum[1][arr[j].l-1]

但是如果两个区间本身有包含 那就要减去sum2的情况了 具体见代码吧

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <cmath>
#include <map>
#include <stack>
#include <set>
#include <sstream>
#include <vector>
#include <stdlib.h>
#include <algorithm>
using namespace std;
#define dbg(x) cout<<#x<<" = "<< (x)<< endl
#define dbg2(x1,x2) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<endl
#define dbg3(x1,x2,x3) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<" "<<#x3<<" = "<<x3<<endl
const int MAX_N = 5025;
int cnt[MAX_N];
struct node
{
    int l,r;
}arr[MAX_N];
int sum[3][MAX_N];
int main()
{
    int n,m,maxx=-1,ans = 0,ans_=-1;
    scanf("%d%d",&n,&m);
    for(int i = 1;i<=m;++i)
    {
        scanf("%d%d",&arr[i].l,&arr[i].r);
        for(int j = arr[i].l;j<=arr[i].r;j++)
            cnt[j]++;
    }
    for(int i = 1;i<=n;++i)
    {
        if(cnt[i]==1) sum[1][i] = sum[1][i-1]+1;
        else sum[1][i] = sum[1][i-1];
        if(cnt[i]==2) sum[2][i] = sum[2][i-1]+1;
        else sum[2][i] = sum[2][i-1];
        if(cnt[i]) ans++;
    }
    for(int i = 1;i<=m;i++)
    {
        for(int j = i+1;j<=m;++j)
        {
            int now = ans - (sum[1][arr[j].r]-sum[1][arr[j].l-1])-(sum[1][arr[i].r]-sum[1][arr[i].l-1]);
            int ll = min(arr[i].r,arr[j].r);
            int rr = max(arr[i].l,arr[j].l);
            if(ll>=rr) now-=(sum[2][ll]-sum[2][rr-1]);
            ans_ = max(ans_,now);
        }
    }
    printf("%d\n",ans_);
    return 0;
}

D. Stressful Training

https://codeforces.com/contest/1132/problem/D

题意 n个人 每个人都有一个电脑 都有初始电量和每分钟使用多少点 你有一个充电器可以给电脑充电 每一秒只能给一台电脑充

电脑电量可以为0 问你最小需要的充电功率

我们容易想到用二分答案 但是check函数需要特别费心思 我们首先把每个电脑是否需要充电判断一下 比如比赛进行m秒 那么你电脑实际上是只用了m-1秒的电的 因为你刚开机不用 所以我们可以让 a[i](初始电量) - (m-1)*b[i] 是否小于0判断是否需要充电 如果需要充电 我们就要让 cnt[a[i]/b[i]+1]++ 代表第几秒充电 并且处理出来 这样我们知道最多可以充电m-1次 并且要满足到第i秒充电的次数不能大于i cnt数组的意思是 cnt[i] 代表第i秒的充电次数 所以我们就可以解决这道题了

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <cmath>
#include <map>
#include <stack>
#include <set>
#include <sstream>
#include <vector>
#include <stdlib.h>
#include <algorithm>
using namespace std;
#define dbg(x) cout<<#x<<" = "<< (x)<< endl
#define dbg2(x1,x2) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<endl
#define dbg3(x1,x2,x3) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<" "<<#x3<<" = "<<x3<<endl
int n,m;
const int MAX_N = 200025;
int cnt[MAX_N];
long long a[MAX_N],b[MAX_N];
bool check(long long mid)
{
    int k = m;
    memset(cnt,0,sizeof(cnt));
    for(int i = 1;i<=n;++i)
    {
        long long tmp = a[i];
        if(tmp>=1ll*m*b[i]) continue;
        cnt[min(tmp/b[i]+1,1ll*(m+1))]++;
        while(tmp<1ll*m*b[i])
        {
            if(k==0) break;
            tmp+=mid;
            cnt[min(tmp/b[i]+1,1ll*(m+1))]++;
            k--;
        }
    }
    long long tot = 0;
    for(int i = 1;i<=m;i++)
    {
        tot += cnt[i];
        if(tot>i) return 0;
    }
    return 1;
}
int main()
{
    scanf("%d%d",&n,&m);
    m--;
    for(int i = 1;i<=n;++i) scanf("%lld",&a[i]);
    for(int i = 1;i<=n;++i) scanf("%lld",&b[i]);
    long long l = 0, r = 1e18;
    while(l<=r)
    {
        long long mid = (l+r)>>1;
        if(check(mid)) r = mid - 1;
        else l = mid + 1;
    }
    if(l>=1e18)
    {
        printf("-1\n");
        return 0;
    }
    else printf("%lld\n",l);
    return 0;
}

E . Knapsack

https://codeforces.com/contest/1132/problem/E

题意 给你一个背包容量 然后1到8的物体有多少个 问题最多背包能装多少重量

如果容量很小 我们可以用完全背包的重量可达去做 也就是下代码中的dp部分

但是容量很大 所以在大范围 我们需要去贪心 贪心就是你想要重量最多 那么你肯定要把小的物品尽量装进来 小范围去更新

小范围如何确定呢 我们发现 lcm(1,2,3,4,5,6,7,8) = 840 也就是说840是最小的都可以取i个的状态 所以我们利用这个性质 把W分成W-840 贪心 840内背包 但是我们知道实际上W-840是不能被完全重量可达的 所以我们要统计一个ans 记录最多有多少重量可达 那么W-ans就是剩下需要去背包的范围 不妨设大一点 840*8即可 分析复杂度 为 8 * 840 * 8  * 840 代码如下

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <cmath>
#include <map>
#include <stack>
#include <set>
#include <sstream>
#include <vector>
#include <stdlib.h>
#include <algorithm>
using namespace std;
#define dbg(x) cout<<#x<<" = "<< (x)<< endl
#define dbg2(x1,x2) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<endl
#define dbg3(x1,x2,x3) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<" "<<#x3<<" = "<<x3<<endl

long long cnt[15],W,Left[15];
bool dp[10][10005];

int main()
{
    long long sum = 0,maxx = 0;
    scanf("%lld",&W);
    for(int i = 1;i<=8;++i) scanf("%lld",&cnt[i]),sum+=cnt[i]*i;
    W = min(sum,W);
    for(int i = 1;i<=8;++i)
    {
        Left[i] = min(cnt[i],840LL/i);
        cnt[i] -= Left[i];
    }

    long long ans = 0;
    long long tot = max(0LL,W-840);
    for(int i = 1;i<=8;++i)
    {
        long long amount = min(cnt[i],(tot-ans)/i);
        ans+= amount*i;
    }
    dp[0][0] = 1;
    for(int i = 1;i<=8;++i)
    {
        for(int j = 0;j<=840*8;j++)
        {
            for(int k = 0;k<=Left[i]&&k*i<=j;k++)
            {
                dp[i][j]|=dp[i-1][j-k*i];
            }
        }
    }
    for(int i = 1;i<=W-ans;i++)
    {
        if(dp[8][i]) maxx = max(maxx,i + ans);
    }
    printf("%lld\n",maxx);
    return 0;
}

F. Clear the String

https://codeforces.com/contest/1132/problem/F

F题题意 给你一组字符串 相同的可以一笔勾销 问你需要几步能删除所有的字符串

关键在于转化题意 你要删除 就是把不一样的颜色化成一样 问题转化一下 这样长度就不用减少 你就可以dp了

这是一个典型的区间dp问题 dp[i][j]代表把第 i 到 第 j 长度的区间内所有字符换成一个颜色需要的代价

那么答案就是dp[1][n] 

我们容易知道 当两端颜色相同 dp[l][r] = min(dp[l][r-1],dp[l+1][r]) 就是把除了端点的其他所有点都化成端点的代价

如果颜色不同 dp[l][r] = min(dp[l][k]+dp[k+1][r],dp[l][r]) (l<=k<=r)

就能解决这题了

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <cmath>
#include <map>
#include <stack>
#include <set>
#include <sstream>
#include <vector>
#include <stdlib.h>
#include <algorithm>
using namespace std;
#define dbg(x) cout<<#x<<" = "<< (x)<< endl
#define dbg2(x1,x2) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<endl
#define dbg3(x1,x2,x3) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<" "<<#x3<<" = "<<x3<<endl
const int MAX_N = 525;
long long dp[MAX_N][MAX_N];
char str[MAX_N];
int main()
{
    int n;
    scanf("%d",&n);
    scanf("%s",str+1);
    memset(dp,0x3f3f3f3f,sizeof(dp));
    for(int i = 1;i<=n;++i)
        dp[i][i] = 1;
    for(int i = 2;i<=n;++i)
    {
        for(int j = 1;j<=n-i+1;j++)
        {
            int l = j,r = j + i - 1;
            if(str[l]==str[r]) dp[l][r] = min(dp[l][r-1],dp[l+1][r]);
            else for(int k = l;k<=r;++k) dp[l][r] = min(dp[l][r],dp[l][k]+dp[k+1][r]);
        }
    }
    printf("%d\n",dp[1][n]);
    return 0;
}

G //做法是学长教的 所以从学长博客扒的 自己实现了下代码

题意

给你一个长度为n的数组,对其中每个长度为k的连续子序列求这个子序列的最长贪心子序列
选定一个数作为第一个数,那么他右面离他最近而且比他大的数作为第二个数,以此类推直到不能再加数这样产生的序列被称为贪心子序列。
对于一个序列选定每一个数作为起点,得到的最长的贪心子序列就是一个序列的最长贪心子序列。

1≤n,k≤106 1 \leq n,k \leq 10^61≤n,k≤10 
6
 
做法

首先这道题一定是滑动窗口来做的。也就是算出第一段的贡献之后,加上一个数的贡献,减去一个数的贡献。
可是我们怎么维护好一些呢,由于要计算每个数靠右的第一个比他大的数,我们这个过程用一个单调栈就能做完。
之后我们把每个数和右边第一个比他大的数建边,我们就可以得到一个森林,再把所有的根同时指向一个虚根,就得到一颗虚树。
我们设树上每个点的权值代表他到虚根这条路径上有多少个点,那么所有点中最大的权值就是答案,那么我们怎么维护加入一个点呢,我们发现,加入一个点的贡献就是把包括他在内的子树中所有点的权值+1,这个用一个基于dfs序的线段树就可以维护,删除一个点的贡献同理,只要将他的子树内所有点的权值-1即可。
 

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <cmath>
#include <map>
#include <stack>
#include <set>
#include <sstream>
#include <vector>
#include <stdlib.h>
#include <algorithm>
using namespace std;
#define dbg(x) cout<<#x<<" = "<< (x)<< endl
#define dbg2(x1,x2) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<endl
#define dbg3(x1,x2,x3) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<" "<<#x3<<" = "<<x3<<endl
const int MAX_N = 1000025;
int in[MAX_N],out[MAX_N],arr[MAX_N],dep,s[MAX_N<<2],col[MAX_N<<2],ans[MAX_N];
stack<int >st;
vector<int > vt[MAX_N];
void dfs(int rt,int fa)
{
    in[rt] = ++dep;
    for(int i = 0;i<vt[rt].size();i++)
    {
        int to = vt[rt][i];
        if(to==fa) continue;
        dfs(to,rt);
    }
    out[rt] = dep;
}
void up(int rt)
{
    s[rt] = max(s[rt<<1],s[rt<<1|1]);
}
void build(int rt,int l,int r)
{
    col[rt] = 0;
    if(l==r)
    {
        s[rt] = 0;
        return ;
    }
    int mid=(l+r)>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
    up(rt);
}
void down(int rt,int l,int r)
{
    if(col[rt])
    {
        int mid = (l+r)>>1;
        col[rt<<1]+=col[rt];
        col[rt<<1|1] +=col[rt];
        s[rt<<1] += col[rt];
        s[rt<<1|1] +=col[rt];
        col[rt] = 0;
    }
}
void update(int rt,int l,int r,int x,int y,int v)
{
    if(x<=l&&r<=y)
    {
        col[rt]+=v;
        s[rt]+=v;
        return ;
    }
    int mid = (l+r)>>1;
    down(rt,l,r);
    if(x<=mid) update(rt<<1,l,mid,x,y,v);
    if(mid<y) update(rt<<1|1,mid+1,r,x,y,v);
    up(rt);
}

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i = 1;i<=n;++i) scanf("%d",&arr[i]);
    build(1,1,n+1);
    for(int i = n;i>=1;i--)
    {
        while(!st.empty()&&arr[st.top()]<=arr[i]) st.pop();
        if(st.empty())
        {
            vt[0].push_back(i);
        }
        else
        {
            vt[st.top()].push_back(i);
        }
        st.push(i);
    }
    dfs(0,-1);
    for(int i = n-m+1;i<=n;++i) update(1,1,n+1,in[i],out[i],1);
    ans[n-m+1] = s[1];
    for(int i = n-m;i>=1;i--)
    {
        update(1,1,n+1,in[i+m],out[i+m],-1);
        update(1,1,n+1,in[i],out[i],1);
        ans[i] = s[1];
    }
    for(int i = 1;i<=n-m+1;++i) i==n-m+1?printf("%d\n",ans[i]):printf("%d ",ans[i]);
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值