蒟蒻补题日记——CF964

108 阅读5分钟

A——Question Marks

统计A,B,C,D四个字符出现的的次数与每次的n取最小值后相加(多次询问记得清空num数组)

#include<bits/stdc++.h>
using namespace std;

int num[6];
void solve()
{
    for(int i=0;i<=4;i++) num[i]=0;
    int n,ans=0;
    cin>>n;
    string s;
    cin>>s;
    for(int i=0;i<s.size();i++)
    {
        if(s[i]=='?') continue;
        num[s[i]-'A']++;

    }
    for(int i=0;i<=4;i++) 
    {
        ans+=min(n,num[i]);
        // cout<<num[i]<<'\n';
    }
    cout<<ans<<'\n';

}

int main()
{
    int t;
    cin>>t;
    while(t--) solve();
    return 0;
}

B——Parity and Sum

首先,按照题目要求,只能选奇偶不同的两个数相加,再将其中较小的那个数替换成两者的和,所以只能是是把偶数变成奇数,最好情况下每次操作都能把一个偶数变成更大的奇数,需要注意的是如果选的偶数比选的奇数大,那奇数就会变成更大的奇数,偶数不变,所以需要维护一个数组里奇数的最大值,首先先拿最大的奇数和偶数从小到大依次比较,如果当前奇数最大值大于当前遍历到的偶数,奇数最大值就要加上当前的偶数,如果比较成功则结果为cnt(偶数的个数),如果失败则为偶数个数+1(一开始就将最大奇数与最大偶数相加)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
ll p[N];
void solve()
{
    int n,cnt=0;
    ll ma=0;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        ll x;
        cin>>x;
        if(x%2==0) p[++cnt]=x;
        else ma=max(x,ma);
    }
    sort(p+1,p+1+cnt);
    if(cnt==n||cnt==0) cout<<0<<'\n';
    else
    {
        int flg=0;
        for(int i=1;i<=cnt;i++)
        {
            if(ma>=p[i]) ma+=p[i];
            else 
            {
                flg=1;
                break;
            }
        }
        if(flg) cout<<cnt+1<<'\n';
        else cout<<cnt<<'\n';
    }

}

int main()
{
    int t;
    cin>>t;
    while(t--) solve();
    return 0;
}

C——Light Switches

首先将P数组从小到大排序,再维护一个从1->n的全开灯区间 d[i]=l,rd[i] = {l , r} , 表示从1到i全开灯区间是l,r{l , r}
分析可知一共有三种情况
1.第i1i-1个区间与第ii个区间相交
2.第i1i-1个区间的等效区间与第ii个区间相交
3.第i1i-1个区间的等效区间不在第ii个区间内
第一种情况 d[i].l=max(d[i1].l,P[i])d[i].l = max( d[i-1].l,P[i])
第二种情况 需要先算出等效区间
左端点dl=d[i1].l+(p[i]r+2t1)/(2t)2tdl=d[i-1].l+(p[i]-r+2*t-1)/(2*t)*2*t
右端点dr=d[i1].l+(p[i]r+2t1)/(2t)2tdr=d[i-1].l+(p[i]-r+2*t-1)/(2*t)*2*t
d[i].l=max(dl,p[i])d[i].l=max(dl,p[i])
d[i].r=min(dr,p[i]+k1)d[i].r=min(dr,p[i]+k-1)
第三种情况直接返回-1

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
ll p[N];
void solve()
{

    int n,t;
    cin>>n>>t;
    ll l=1,r=t;
    int flg=0;
    for(int i=1;i<=n;i++) cin>>p[i];
    sort(p+1,p+1+n);
    l=p[1],r=p[1]+t-1;
    for(int i=2;i<=n;i++)
    {
        // cout<<l<<' '<<r<<'\n';
        if(r>=p[i])
        {
            l=max(l,p[i]);
            continue;
        }
        else
        {
            ll num=((p[i]-r+2*t-1)/(2*t));
            // cout<<num<<'\n';
            ll dl=l+2*t*num,dr=r+2*t*num;
            if(dl<=p[i]+t-1)
            {
                l=max(p[i],dl);
                r=min(p[i]+t-1,dr);
            }
            else
            {
                cout<<"-1\n";
                return;
            }
        }
        // cout<<l<<' '<<r<<'\n';

    }
    cout<<l<<"\n";
}

int main()
{
    int t;
    cin>>t;
    while(t--) solve();
    return 0;
}

D——Med-imize

参考题解CF1993D 题解 - 洛谷专栏 (luogu.com.cn)
首先二分中位数mid,选定中位数后将数组里大于中位数的置为1,小于中位数的置为-1,二分答案找删除(m1)/n⌊(m-1)/n⌋个长度为m的连续区间后剩下的数的和的最大值,如果大于0则返回ture,小于0则返回false

方法一:记忆化搜索
dfs(u,k)dfs(u,k):求解从1到u,删除k段长度为m的区间后所剩下的数的和的最大值
dp[i][j]dp[i][j]:表示从1到i,删除(i1)/n+j⌊(i-1)/n⌋+j个长度为m的连续区间后剩下的数的和的最大值
记忆化搜索降低时间复杂度

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
typedef long long ll;

#define ios ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);

ll p[N],d[N];
ll dp[N][2],vis[N][2];
int n,m;

ll dfs(ll u,ll k)
{
    if (u<0||k<0||m*k>u) return -1e9;
    if(!u) return 0;
    int t;
    t=k-(u-1)/m;

    if(vis[u][t]) return dp[u][t];
    vis[u][t]=1;
    dp[u][t]=max(dfs(u-1,k)+d[u],dfs(u-m,k-1));
    return dp[u][t]; 
}

bool check(ll u)
{
    for(int i=1;i<=n;i++)
    {
        vis[i][0]=vis[i][1]=0;
        if(p[i]<u) d[i]=-1;
        else d[i]=1;
    }
    int num=(n-1)/m;
    int cnt=dfs(n,num);
    if(cnt>0) return 1;
    return 0;
}

void solve()
{
    ll l=1,r=0;
    cin>>n>>m;
    for(int i=1;i<=n;i++) 
    {
        cin>>p[i];
        r=max(r,p[i]);
    }
    while(l<r)
    {
        ll mid=(l+r+1)/2;
        if(check(mid)) l=mid;
        else r=mid-1;
    }
    cout<<l<<'\n';
}

int main()
{
    ios
    int t;
    cin>>t;
    while(t--) solve();
    return 0;
}

方法二:线性DP
dp[i]dp[i]表示尽可能多的删除长度为m的连续区间后剩下的数字(最多剩下m个)之和的最大值

using namespace std;
const int N=5e5+10;
typedef long long ll;

#define ios ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);

ll p[N],d[N];
ll dp[N];
int n,m;

// ll dfs(ll u,ll k)
// {
//     if (u<0||k<0||m*k>u) return -1e9;
//     if(!u) return 0;
//     int t;
//     t=k-(u-1)/m;

//     if(vis[u][t]) return dp[u][t];
//     vis[u][t]=1;
//     dp[u][t]=max(dfs(u-1,k)+d[u],dfs(u-m,k-1));
//     return dp[u][t]; 
// }

bool check(ll u)
{
    for(int i=1;i<=n;i++)
    {
        if(p[i]<u) d[i]=-1;
        else d[i]=1;
    }
    for(int i=1;i<=n;i++)
    {
        if(i!=1&&(i-1)%m==0) dp[i]=max(dp[i-m],d[i]);
        else
        {
            dp[i]=dp[i-1]+d[i];
            if(i-1>m) dp[i]=max(dp[i],dp[i-m]);
        }
    }
    if(dp[n]>0) return 1;
    return 0;
}

void solve()
{
    ll l=1,r=0;
    cin>>n>>m;
    for(int i=1;i<=n;i++) 
    {
        cin>>p[i];
        r=max(r,p[i]);
    }
    while(l<r)
    {
        ll mid=(l+r+1)/2;
        if(check(mid)) l=mid;
        else r=mid-1;
    }
    cout<<l<<'\n';
}

int main()
{
    ios
    int t;
    cin>>t;
    while(t--) solve();
    return 0;
}