• Educational Codeforces Round 61题解


    A题

    首先)(其实相当于1个即可

    所以如果1和4相等并且大于等于1即可

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pll;
    const int N=5e5+10;
    int main(){
        ios::sync_with_stdio(false);
        int a,b,c,d;
        cin>>a>>b>>c>>d;
        c=min(c,1);
        if(a==d&&a>=c){
            cout<<1<<endl;
        }
        else{
            cout<<0<<endl;
        }
    }
    View Code

    B题

    直接找到K大数就行

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pll;
    const int N=5e5+10;
    int n,m;
    int a[N],b[N];
    int main(){
        ios::sync_with_stdio(false);
        cin>>n;
        int i;
        ll sum=0;
        for(i=1;i<=n;i++){
            cin>>a[i];
            sum+=a[i];
        }
        sort(a+1,a+1+n);
        reverse(a+1,a+1+n);
        cin>>m;
        for(i=1;i<=m;i++){
            cin>>b[i];
        }
        for(i=1;i<=m;i++){
            cout<<sum-a[b[i]]<<endl;
        }
    }
    View Code

    C题

    首先发现就是删掉两个,因此直接枚举两个人

    写了发线段树出了点小问题,后来改成直接前缀和维护1的个数,因为只有区间内的1才会消失

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pll;
    const int N=2e5+10;
    int cnt[N];
    int sum[N];
    struct node{
        int l,r;
    }s[N];
    bool cmp(node a,node b){
        if(a.l==b.l)
            return a.r<b.r;
        return a.l<b.l;
    }
    int main(){
        ios::sync_with_stdio(false);
        int n,m;
        cin>>n>>m;
        int i,j;
        for(i=1;i<=m;i++){
            cin>>s[i].l>>s[i].r;
            for(j=s[i].l;j<=s[i].r;j++)
                cnt[j]++;
        }
        sort(s+1,s+1+m,cmp);
        int ans=0;
        for(i=1;i<=m;i++){
            for(j=s[i].l;j<=s[i].r;j++){
                cnt[j]--;
            }
            int tmp=0;
            for(j=1;j<=n;j++){
                if(cnt[j]==1){
                    sum[j]=sum[j-1]+1;
                }
                else{
                    sum[j]=sum[j-1];
                }
                if(cnt[j])
                    tmp++;
            }
            for(j=i+1;j<=m;j++){
                ans=max(ans,tmp-sum[s[j].r]+sum[s[j].l-1]);
            }
            for(j=s[i].l;j<=s[i].r;j++)
                cnt[j]++;
        }
        cout<<ans<<endl;
    }
    View Code

    D题

    这次二分答案是肯定的,在check的时候,其实刚开始想的是复杂度低的正解,也就是找到每个点最晚什么时候要+,之后不断往后跳到K位置,之后找前缀和,如果每个i的前缀和大于i,说明前i个时间点

    要大于i的次数充电,那么显然是非法的,这里用了一个贪心+前缀和,可惜写的时候没想清楚,没写出来这个写法,主要是在找到第一次最晚后,不知道下面继续怎么做

    另一种用优先队列维护的姿势比较好想,就是每次找到能撑天数最少的点充电,如果发现不行就返回false,这种写法复杂度较高,理论上能过,但是需要一些优化的姿势,我是卡过去的

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pll;
    const int N=2e5+10;
    int n,k;
    ll a[N],b[N];
    struct node{
        ll x,y;
        ll k;
        bool operator <(const node &t) const{
            if(k!=t.k)
                return k>t.k;
            if(y!=t.y)
                return y>t.y;
            return x>t.x;
        }
    };
    bool check(ll x){
        priority_queue<node> q;
        int i;
        for(i=1;i<=n;i++){
            ll tmp=a[i]/b[i];
            q.push({a[i],b[i],tmp});
        }
        if(q.empty())
            return true;
        for(i=1;i<=k;i++){
            auto t=q.top();
            q.pop();
            if(t.k<i-1)
                return false;
            if(t.k>k)
                return true;
            q.push({t.x+x,t.y,(t.x+x)/t.y});
        }
        return true;
    }
    int main(){
        ios::sync_with_stdio(false);
        cin>>n>>k;
        int i;
        for(i=1;i<=n;i++)
            cin>>a[i];
        for(i=1;i<=n;i++)
            cin>>b[i];
        ll l=0,r=2e12;
        while(l<r){
            ll mid=l+r>>1;
            if(check(mid))
                r=mid;
            else
                l=mid+1;
        }
        if(l==2e12)
            cout<<-1<<endl;
        else
            cout<<l<<endl;
    }
    优先队列
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pll;
    const int N=2e5+10;
    int n,k;
    ll a[N],b[N];
    int cnt[N];
    bool check(ll x){
        memset(cnt,0,sizeof cnt);
        int tot=0;
        int i;
        for(i=1;i<=n;i++){
            ll tmp=a[i]/b[i];
            tmp+=1;
            ll t=a[i];
            while(tmp<k&&tot<k){
                cnt[tmp]++;
                ++tot;
                t+=x;
                tmp=(t/b[i])+1;
            }
            if(tot==k)
                return false;
        }
        for(i=1;i<=k;i++){
            cnt[i]+=cnt[i-1];
            if(cnt[i]>i)
                return false;
        }
        return true;
    }
    int main(){
        ios::sync_with_stdio(false);
        cin>>n>>k;
        int i;
        for(i=1;i<=n;i++)
            cin>>a[i];
        for(i=1;i<=n;i++)
            cin>>b[i];
        ll l=0,r=2e12;
        while(l<r){
            ll mid=l+r>>1;
            if(check(mid))
                r=mid;
            else
                l=mid+1;
        }
        if(l==2e12)
            cout<<-1<<endl;
        else
            cout<<l<<endl;
    }
    贪心+前缀和

    E题

    这题确实转化比较巧妙,但是对应的思路其实以前出现过,就是找到lcm的值,我记得以前有一个青蛙跳石子也是这样的,需要牢记一下。

    我们看到这个题会发现需要剪枝或者转化,并不是简单的背包dp,那么这里就有一步重要的转化,我们发现只有1-8这些数,他们的最小公倍数是840,而每个值取法都能表示为 c=840/i*p+q,where 0q<840/i

    因此我们维护dp状态为f[i][j],表示前i个权值取到j容量可以取到最大的840个数是多少。

    之后就是dp转移,然后枚举状态取max,注意不能超过最大值w

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pll;
    const int N=2e5+10;
    ll cnt[N];
    ll f[10][840*8+10];
    int main(){
        ios::sync_with_stdio(false);
        ll w;
        memset(f,-1,sizeof f);
        cin>>w;
        int i,j,k;
        ll ans=0;
        for(i=1;i<=8;i++){
            cin>>cnt[i];
            ans+=cnt[i]*i;
        }
        if(ans<=w){
            cout<<ans<<endl;
            return 0;
        }
        f[0][0]=0;
        for(i=1;i<=8;i++){
            for(j=0;j<=840*8;j++){
                if(f[i-1][j]!=-1){
                    ll tmp=840/i;
                    tmp=min(tmp,cnt[i]);
                    for(k=0;k<=tmp;k++){
                        f[i][j+k*i]=max(f[i][j+k*i],f[i-1][j]+(cnt[i]-k)/(840/i));
                    }
                }
            }
        }
        ans=0;
        for(i=0;i<=840*8;i++){
            if(i>w)
                break;
            if(f[8][i]!=-1){
                ans=max(ans,i+min(f[8][i],(w-i)/840)*840);
            }
        }
        cout<<ans<<endl;
    }
    View Code

    F题

    比较朴素的区间dp,只要三维之后看看前后点是否相同,相同就能-1,别人的解法是中间点和末尾的比较,我们直接比较前后是一样的,虽然中间转移的时候可能状态不一样,但是结果是相等的

    这是因为我们小区间的开头其实就是大区间的中间点,这和别人的转移是一样的。

    前后相同就能-1就是因为可以先删中间。因为我们枚举到了所有状态,所以转移是正确的

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pll;
    const int N=5e5+10;
    int f[510][510];
    int main(){
        ios::sync_with_stdio(false);
        int n;
        string s;
        cin>>n>>s;
        int i,j,k;
        s=" "+s;
        memset(f,0x3f,sizeof f);
        for(int len=1;len<=n;len++){
            for(i=1;i+len-1<=n;i++){
                j=i+len-1;
                if(len==1){
                    f[i][i]=1;
                    continue;
                }
                for(k=i;k<j;k++){
                    f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]-(s[i]==s[j]));
                }
            }
        }
        cout<<f[1][n]<<endl;
    }
    View Code

     G题

    想了有些时间,终于发现了题目的性质,但是在最后求解的时候犯糊涂了

    心路历程:

    正向看了一下,发现不太行,转移状态过多,那么正难则反,反向看,又结合题目当中的信息,一个点必须要连后面最接近他的比他大的点

    这么一看,每个点只有一个父亲节点,而之前往前看的时候注意到每个点有很多儿子节点,那不就是树吗

    于是想到单调栈建树。建完之后一个想法就呼之欲出,每个点的贡献就是给他和他的儿子长度+1,容易想到dfs序建线段树实现区间加

    想到这里我又犯模糊了,真实区间又不是连续的,怎么求最大值,其实这是线段树最简单的套路,打的时候有些意识模糊。因为现在每个点的含义就是当前状态下的这个点能连出的最长路,因此只要查询tr[1].mx就能找到全部区间内的最大值

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pll;
    const int N=2e6+10;
    int n,k;
    int h[N],ne[N],e[N],idx;
    int dfn[N],id[N],sz[N],times;
    vector<int> ans;
    int rt;
    int q[N];
    int a[N];
    struct node{
        int l,r;
        int mx;
        int lazy;
    }tr[N<<2];
    void add(int a,int b){
        e[idx]=b,ne[idx]=h[a],h[a]=idx++;
    }
    void dfs(int u){
        int i;
        dfn[u]=++times;
        id[times]=u;
        sz[u]=1;
        for(i=h[u];i!=-1;i=ne[i]){
            int j=e[i];
            dfs(j);
            sz[u]+=sz[j];
        }
    }
    void build(int u,int l,int r){
        if(l==r){
            tr[u]={l,r};
        }
        else{
            tr[u]={l,r};
            int mid=l+r>>1;
            build(u<<1,l,mid);
            build(u<<1|1,mid+1,r);
        }
    }
    void pushup(int u){
        tr[u].mx=max(tr[u<<1].mx,tr[u<<1|1].mx);
    }
    void pushdown(int u){
        int x=tr[u].lazy;
        tr[u<<1].mx+=x;
        tr[u<<1|1].mx+=x;
        tr[u<<1].lazy+=x;
        tr[u<<1|1].lazy+=x;
        tr[u].lazy=0;
    }
    void modify(int u,int l,int r,int x){
        if(tr[u].l>=l&&tr[u].r<=r){
            tr[u].mx+=x;
            tr[u].lazy+=x;
            return ;
        }
        if(tr[u].lazy)
            pushdown(u);
        int mid=tr[u].l+tr[u].r>>1;
        if(l<=mid)
            modify(u<<1,l,r,x);
        if(r>mid)
            modify(u<<1|1,l,r,x);
        pushup(u);
    }
    int main(){
        ios::sync_with_stdio(false);
        cin>>n>>k;
        int i;
        rt=n+1;
        memset(h,-1,sizeof h);
        for(i=1;i<=n;i++){
            cin>>a[i];
        }
        int hh=0,tt=-1;
        for(i=1;i<=n;i++){
            while(hh<=tt&&a[q[tt]]<a[i]){
                add(i,q[tt]);
                tt--;
            }
            q[++tt]=i;
        }
        while(hh<=tt){
            add(rt,q[tt]);
            tt--;
        }
        dfs(rt);
        build(1,1,n+1);
        for(i=1;i<=k;i++){
            modify(1,dfn[i],dfn[i]+sz[i]-1,1);
        }
        ans.push_back(tr[1].mx);
        for(i=k+1;i<=n;i++){
            modify(1,dfn[i-k],dfn[i-k]+sz[i-k]-1,-1);
            modify(1,dfn[i],dfn[i]+sz[i]-1,1);
            ans.push_back(tr[1].mx);
        }
        for(auto x:ans){
            cout<<x<<" ";
        }
        cout<<endl;
    }
    View Code
    没有人不辛苦,只有人不喊疼
  • 相关阅读:
    设计模式-1.12备忘录模式
    设计模式-简介
    设计模式-1.9享元模式
    设计模式-1.8组合模式
    设计模式-1.7外观模式
    设计模式-1.6建造者模式(生成器模式)
    设计模式-1.5原型模式
    我在GitHubPage的博客
    奇怪的友链增加啦!
    SSL-OI夏日合宿 杂题 LOJ#6089小Y的背包计数问题 根号分治
  • 原文地址:https://www.cnblogs.com/ctyakwf/p/14141475.html
Copyright © 2020-2023  润新知