反思之前,确实太水了,不想再水下去了,菜鸡不能永远是菜鸡,以后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; }