Codeforces Good Bye 2025 Div1+2(ABCDE)

前言

期末周好几天忙复习都没写题,感觉什么都不会了呜呜TT

赛时牢骚:牛魔的舍友简直不是人,他妈从十点开始开麦打游戏,一边打一边吼。我带着降噪耳机音乐声开最大都听得一清二楚,吵得我脑子一团浆糊,真的草了......

一、A. Yes or Yes

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

/*   /\_/\
*   (= ._.)
*   / >  \>
*/

/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/

#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};

void solve()
{
    string s;
    cin>>s;
    int n=s.length();
    s=" "+s;

    int yes=0;
    for(int i=1;i<=n;i++)
    {
        if(s[i]=='Y')
        {
            yes++;
        }
    }

    if(yes>=2)
    {
        NO;
    }
    YES;
}

void init()
{
}

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t=1;
    cin>>t;
    init();
    while(t--)
    {
        solve();    
    }
    return 0;
}

因为是或运算,所以过程中只要有一个Y,那么以后就都是Y了。那么当来到最后一步时,可以发现只要字符串中Y的个数大于等于两个,那么合并到最后一定剩下两个Y,此时就不合法了。所以如果想让合并的过程中不出现两个Y,那么就要求Y的个数最多只能有一个。

二、B. Impost or Sus

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

/*   /\_/\
*   (= ._.)
*   / >  \>
*/

/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/

#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};

void solve()
{
    string s;
    cin>>s;
    int n=s.length();
    s=" "+s;

    int pre=0;
    int ans=0;
    for(int i=1;i<n;i++)
    {
        if(s[i]=='u')
        {
            if(pre)
            {
                pre=0;
            }
            else
            {
                ans++;
                pre=1;
            }
        }
        else
        {
            pre=1;
        }
    }

    if(s[n]=='u')
    {
        ans++;
    }

    cout<<ans<<endl;
}

void init()
{
}

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t=1;
    cin>>t;
    init();
    while(t--)
    {
        solve();    
    }
    return 0;
}

byd这题翻译内鬼了还被硬控了一会儿......

看原文,意思就是要求对于每个u,距离其最近的两个s字符到其的距离相同,所以其实就是要求这两个s字符关于这个u字符对称。那么之后考虑往中间塞字符,因为两边距离最近的s字符已经确定,所以如果塞s字符的话就会破坏这个条件。而如果往里塞u的话,可以发现此时就必然不可能满足这两个s字符到其的距离相同。所以就可以得出,唯一合法的条件就是"sus"这样。所以之后按照这种方式判断是否需要改即可,注意如果最后的字符是u的话需要多改一次。

三、C. First or Second

这道题告诉我们有时候不要相信第一感觉和常识,还是需要就题分析......

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

/*   /\_/\
*   (= ._.)
*   / >  \>
*/

/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/

#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};

//赛时傻逼解
void solve1()
{
    int n;
    cin>>n;
    vector<ll>a(n+1);
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }

    vector<ll>dp(n+1);

    dp[1]=a[1];
    ll pre=dp[1];
    for(int i=2;i<=n;i++)
    {
        dp[i]=pre+a[i];

        if(dp[i]>pre)
        {
            pre=dp[i];
        }
        else
        {
            pre-=a[i];
        }
    }

    vector<ll>suf(n+2);
    for(int i=n;i>=1;i--)
    {
        suf[i]=suf[i+1]+a[i];
    }

    ll ans=-INFLL;
    for(int i=1;i<=n;i++)
    {
        ans=max(ans,dp[i]-suf[i]);
    }
    cout<<ans<<endl;
}

//正解
void solve2()
{
    int n;
    cin>>n;
    vector<ll>a(n+1);
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }

    //若选择让第i个人留下
    //那么其后面的所有人只能作为第二个人被减去
    //其前面的人除了第一个人必须加,剩下的人全部可以取得正贡献
    //方法是可以考虑每次让正数作为第一个人
    //先把到下一个正数前的所有负数减去,再选择当前正数

    vector<ll>pre(n+1);
    pre[1]=a[1];
    for(int i=2;i<=n;i++)
    {
        pre[i]=pre[i-1]+abs(a[i]);
    }

    vector<ll>suf(n+2);
    for(int i=n;i>=1;i--)
    {
        suf[i]=suf[i+1]+a[i];
    }

    ll ans=-INFLL;
    for(int i=1;i<=n;i++)
    {
        ans=max(ans,pre[i-1]-suf[i+1]);
    }
    cout<<ans<<endl;
}

void init()
{
}

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t=1;
    cin>>t;
    init();
    while(t--)
    {
        solve2();    
    }
    return 0;
}

赛时这个傻逼解就不说了,太有病了......

首先,我承认这个题第一眼确实是dp,但经过分析后需要发现dp做不了或者非常复杂。()之后,因为正着考虑会比较复杂,那么就考虑从最后开始分析。因为最后必然会剩下一个人,那么对于剩下的这个人,可以发现其右侧的人必然是全部作为第二个人减去的。而对于其左侧的人,首先可以发现第一个人必然是作为第一个人加上的。之后,因为想要结果最大,那么此时若存在若干个连续的负数,那么就都可以作为第二个人减去,从而取得正贡献。此时,第二个位置就是正数了,那么此时就可以加上第一个人,然后让这个正数作为第一个人,那么之后就可以取得正贡献了。所以以此类推,对于最后剩下的这个人的左侧,是全可以取到正贡献的。所以只需要维护一个前缀绝对值和一个后缀和,然后枚举哪个人最后剩下取最大值即可。

四、D. Xmas or Hysteria

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

/*   /\_/\
*   (= ._.)
*   / >  \>
*/

/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/

#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};

void solve()
{
    int n,m;
    cin>>n>>m;
    vector<array<ll,2>>a(n+1);
    for(int i=1;i<=n;i++)
    {
        cin>>a[i][0];
        a[i][1]=i;
    }

    if(m>n/2)
    {
        cout<<-1<<endl;
        return ;
    }

    sort(a.begin()+1,a.end());

    if(m==0)
    {
        vector<pii>ans;

        ll sum=0;
        int i=n-1;
        while(i>=1&&sum+a[i][0]<a[n][0])
        {
            ans.push_back({a[i][1],a[n][1]});
            sum+=a[i][0];
            i--;
        }

        if(i==0)
        {
            cout<<-1<<endl;
            return ;
        }

        for(int j=1;j<i;j++)
        {
            ans.push_back({a[j][1],a[j+1][1]});
        }
        ans.push_back({a[i][1],a[n][1]});

        cout<<ans.size()<<endl;
        for(auto [i,j]:ans)
        {
            cout<<i<<" "<<j<<endl;
        }
        return ;
    }

    vector<pii>ans;
    for(int i=1;i<=n-2*m;i++)
    {
        ans.push_back({a[i][1],a[i+1][1]});
    }

    for(int i=n-m+1,j=n-2*m+1;i<=n;i++,j++)
    {
        ans.push_back({a[i][1],a[j][1]});
    }

    cout<<ans.size()<<endl;
    for(auto [i,j]:ans)
    {
        cout<<i<<" "<<j<<endl;
    }
}

void init()
{
}

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t=1;
    cin>>t;
    init();
    while(t--)
    {
        solve();    
    }
    return 0;
}

因为每次攻击必然导致一只精灵死亡,一只精灵再也无法攻击。所以若想要m只精灵最后存活,那么就有n-m只精灵死亡。那么就必须进行n-m次攻击,同时会有n-m只精灵再也无法攻击。又因为需要保证存活下来的精灵全攻击过,所以如果想要存活的精灵最多,那么就是当n-m恰好等于m的时候,即存活下来的精灵全攻击过,所以若n小于2*m就不可能实现。

考虑对精灵按攻击力从小到大排序,那么肯定是攻击力最大的m个精灵更有可能活下来,所以考虑让最大的m个精灵攻击剩下的精灵。由于这样只能打死剩下的m个精灵,会有精灵没被攻击过。所以需要考虑先让剩下的精灵攻击到只剩m个,此时再让最大的m个精灵打一遍即可。对于剩下的精灵,可以考虑链式攻击,就是每次让小的去攻击大的,直到让剩下的精灵只剩m个。

特别地,若要求所有小精灵全死,首先肯定需要保证血量最厚的小精灵能被打死。所以可以考虑先从大到小让这些精灵都攻击最大的,那么若打到最后都没法把最大的打死,就不可能实现。否则,那么就找到最后一下打死最大值的小精灵i,然后对于其左侧的精灵,同样链式攻击。然后再让这个精灵攻击最大值,同归于尽即可。

五、E. Flatten or Concatenate

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

/*   /\_/\
*   (= ._.)
*   / >  \>
*/

/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/

#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};

ll ask(int l,int r)
{
    cout<<"? "<<l<<" "<<r<<endl;
    ll sum;
    cin>>sum;
    return sum;
}

void solve()
{
    int n;
    cin>>n;

    int L=1;
    int R=n;
    ll cur=ask(1,n);
    while(L<R)
    {
        int l=L;
        int r=R;
        int m;
        while(l<=r)
        {   
            m=l+r>>1;

            ll res=ask(L,m);

            if(res==cur/2)
            {
                cur/=2;
                
                if(m-L+1<=R-m)
                {
                    R=m;
                }
                else
                {
                    L=m+1;
                }
                break;
            }
            
            if(res>cur/2)
            {
                r=m-1;
            }
            else
            {
                l=m+1;
            }
        }
    }

    ll ans=ask(L,L);
    cout<<"! "<<ans<<endl;
}

void init()
{
}

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t=1;
    cin>>t;
    init();
    while(t--)
    {
        solve();    
    }
    return 0;
}

首先还是看操作次数,因为n很大而300次非常少,那么就需要考虑logn的做法。之后不难发现,不管怎么分裂,整个数组的累加和是不会变的。那么每拼接一次,整个数组的累加和就会变成原来的两倍,那么其实就可以用二分去找每次拼接的位置。

之后,因为操作一每次只会分裂当前数组内最大的元素,又因为每次分裂后都会使数组增加1长度,所以数组越长表明分裂的次数越多。又因为两数组初始是一样的,所以对于两个数组,长度更短的那个数组的最大值肯定不会小于长度更长的数组。那么当二分找到拼接的位置时,若左侧的长度更短,那么就说明最大值一定在左侧数组,否则就说明最大值一定在右侧数组。

所以整体思路就是二分套二分,外面的二分表示当前所在的范围,每次去内部二分找累加和一半位置,然后根据左右侧大小考虑之后去一侧继续二分,最后结束位置的就是整个数组的最大值。因为n是1e5的,那么二分的复杂度logn就是17,17的平方等于289,所以是可以的。

总结

先忙期末,忙完期末寒假开始加加加训!!

END

相关推荐
我在人间贩卖青春21 分钟前
C++之this指针
c++·this
云姜.23 分钟前
java多态
java·开发语言·c++
CoderCodingNo32 分钟前
【GESP】C++五级练习题 luogu-P1865 A % B Problem
开发语言·c++·算法
陳103039 分钟前
C++:红黑树
开发语言·c++
大闲在人43 分钟前
7. 供应链与制造过程术语:“周期时间”
算法·供应链管理·智能制造·工业工程
一切尽在,你来44 分钟前
C++ 零基础教程 - 第 6 讲 常用运算符教程
开发语言·c++
小熳芋1 小时前
443. 压缩字符串-python-双指针
算法
Charlie_lll1 小时前
力扣解题-移动零
后端·算法·leetcode
chaser&upper1 小时前
矩阵革命:在 AtomGit 解码 CANN ops-nn 如何构建 AIGC 的“线性基石”
程序人生·算法
weixin_499771551 小时前
C++中的组合模式
开发语言·c++·算法