反思之前,确实太水了,不想再水下去了,菜鸡不能永远是菜鸡,以后cf坚持写题解,加油干呗

A:给你一个n*m大的矩阵,对于矩阵中任何一点,每一步,可以向上下左右走或者停在原地,问所有点要走到给定的(r,c)最少多少步?
题解:就画一个图,我们先走x方向然后走y方向,然后在x方向上,取max(r-1,n-r),表示x方向上最多走多少步,在y方向上,取max(c-1,m-c),然后两个max相加即可。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n,m,r,c;
        cin>>n>>m>>r>>c;
        cout<<max(r-1,n-r)+max(c-1,m-c)<<endl;    
    }
    return 0;
}

B:给你n个数ci(1<=ci<=100),每次可以把其中k个数变成同一个数,问最少多少次能够把所有数变成一样的。

题解:ci取值非常小,直接枚举把所有数变成某个ci(从1到100),再从头开始枚举数组,需要多少步能把所有数变成一样的,10^7可以过的。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+5;
int c[N];
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n,k;
        cin>>n>>k;
        for(int i=1;i<=n;i++)
        {
            cin>>c[i];
        }
        int ans=(int)1e9;
        int res;
        for(int i=1;i<=100;i++)
        {
            res=0;
            for(int j=1;j<=n;)
            {
                if(c[j]!=i)
                {
                    res++;
                    j+=k;
                }
                else 
                {
                    j++;
                }
            }
            ans=min(ans,res);
         } 
         cout<<ans<<endl;
    }
    return 0;
}

C:给你长度为n(10^5)的字符串a1-an(ai为0或1,1表示该位置有柱子,0没有),给你一个p,一个k,代表从第p个开始跳,每次跳到该位置的下k个位置,即第p+k,p+2k...,再给你一个x,y代表删掉最开头那个字符的花费,x代表把某个位置的0改变成1的花费,问要跳出最后一个位置最少需要多少步。

题解:我们用类似dp的思想,如果a[i]=='0',i位置开始总比i+k开始要多变一次,dp[i]=dp[i+k]+(a[i]=='0').
现在我们求出了从每个位置跳到末尾要变多少次。然后遍历所有a[i],求从a[i]开始的花费,同时更新最小值。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=3e5+5;
ll dp[N];
char a[N];
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        memset(dp,0,sizeof(dp)); 
        int n,p,k;
        cin>>n>>p>>k;
        cin>>a+1;
        ll x,y;
        cin>>x>>y;
        for(int i=n;i>=p;i--)
        {
            dp[i]=dp[i+k]+(a[i]=='0');
        }
        ll ans=0x7f7f7f7f7f7f7f7f;
        for(int i=p;i<=n;i++)
        {
            ans=min(ans,(i-p)*y+dp[i]*x);
        }
        cout<<ans<<endl;
    }
    return 0;
}

D:给你n个数,每次操作可以选择其中连续的两个数,把他们变成一个数-它们的异或和,为最少多少次操作,才能使这个序列不再是非严格单调增序列

题解:异或和,就去想二进制表达嘛,然后变成不再是非严格单调增序列,就是从第二个数到最后一个数,存在一个数大于其左边或者小于其右边,考虑一下最高位,因为是非严格单增的,所以前面的数的最高位不可能比后面大,每个ai<10^9,约32位,那么当n>=96时,必然存在连续三个最高位相同,三个数中的后两个异或和比前面小,这时答案必定是1,n<96时直接暴力,枚举起点,终点,从起点到终点遍历一遍,更新答案。(我是憨批,为什么写代码的时候只考虑一段区间的左右两点,而不是左右两段,好好去反思反思*-*)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=3e5+5;
ll dp[N];
ll a[N];
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    if(n>=96) cout<<1<<endl;
    else{
        int f=0;
        ll ans=0x7f7f7f7f7f7f7f7f;
        for(ll i=1;i<=n-1;i++)
        {
            for(ll j=i+1;j<=n;j++)
            {
                ll temp=0;
                for(ll k=i;k<=j;k++)
                {
                    temp^=a[k];
                }
                ll temp1=0;
                for(ll k=i-1;k>=1;k--)
                {
                    temp1^=a[k];
                    if(temp<temp1)
                    {
                        f=1;
                        ans=min(ans,j-i+i-1-k);
                    }
                }
                ll temp2=0;
                for(ll k=j+1;k<=n;k++)
                {
                    temp2^=a[k];
                    if(temp>temp2)
                    {
                        f=1;
                        ans=min(ans,j-i+k-j-1);
                    }
                }
                //cout<<i<<' '<<j<<' '<<temp1<<' '<<temp2<<endl;
            }
        }
        if(f)
        cout<<ans<<endl;
        else cout<<-1<<endl;
    }
    //ll fin1=11^22,fin2=71^92;
    //cout<<fin1<<' '<<fin2<<endl;
    return 0;
}

E:题意就是给你n个数ci,和一个k,开始基金为0,设为res,设所要求的为sum,每次res加一个ci,sum+=res;如果res小于0的时候,可以重置res为0,并且这次加的ci不算,最多重置k次,问把所有ci加完,sum最大值是多少。
题解:我做这题的时候又去***了...明显,要使sum最大,肯定是把正数全部加上,而且正数越大的,贡献越大,此时也不使用重置,所以首先从大到小排个序,然后res和sum一直加,当res加到负之前,sum还是一直加res,res<0时,就把它和后面没加的负的ci放在一起考虑,一群负数,我们现在有k次机会把res置0,首先想的是负的越多的贡献让它尽可能小(我当时想的是把最小的就用置0,其他的按数从大到小贡献也越来越小,这样子就是从小到大排序,然后后面几个全部用0,后面也发现了bug,想错了,因为我还是没有做到让越小的所有数的贡献尽可能小),但是自己当时也没有平均分这个思想,还是太菜了。。。我们有k次置0机会,但是最后一个数咱们不算啊,就可以想想分成k+1组,把最小的那k+1个放到每一组的最后一个,其次小的k+1个放在每一组的倒数第二个...想想是不是最优的做法呢,答案是肯定的。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=5e5+5;
ll c[N];
int main()
{
    int n,k;
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    {
        cin>>c[i];
    }
    sort(c+1,c+1+n,greater<ll>());
    ll sum=0,res=0;
    int i;
    for(i=1;i<=n;i++)
    {
        sum+=res;
        res+=c[i];
        if(res<0) break;
    }
    vector<ll>q;
    q.push_back(res);
    for(i++;i<=n;i++)
    {
        q.push_back(c[i]); 
    }
    sort(q.begin(),q.end());
    for(int j=0;j<q.size();j++)
    {
        sum+=(ll)q[j]*(j/(k+1));
     } 
     cout<<sum<<endl;
    return 0;
}