菜菜cf题解(不想拖后腿总结版)

问题直接

  1. 去你大爷的long long问题!!!!!!!!!!!!!!
  2. 当是26个字母时,优先使用数组[26];
  3. 像map,set之类的stl都是有一定时间复杂度的,所以能用vector和arr最好用这两个
  4. 利用前缀和时,因为左边l要-1,所以记住(1)数组要从i=1开始放。(2)l 的初始化是1不是0!

961的B1/B2

在这里插入图片描述

题目

题目的大概意思为
给你一个数n,与一个总数m;
再分别给你n个数a1,a2,a3…,an。
要求求一个数,为a数组数的相加,要是ai和aj的数相差不大于一,且总值小于m。求这个数的最大值。

这道题的难点在于,知道a个x,b个y,m,求如何ans=a1x+b1y<=m,求ans最大。

思路

b1有很多种思路,可以用B2的思路,也可以

1.双指针

先将数组进行排序
用一个数j来记录开始,i来记录当前位置,然后,里面的x相加,如果超过了(a[i]-a[j]>1或x>m),就将前面的a[j]位置的数减去,用while循环!

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve() {
	ll n, m;
	cin >> n >> m;
	ll a[n+5];
	for(int i=1; i<=n; i++) cin>>a[i];
	sort(a+1,a+n+1);
	ll ans=0,i=0,j=1,x=0;
	while(i<=n) {
		i++;
		x+=a[i];
		while(x>m||a[i]-a[j]>1) {
			x-=a[j];
			j++;
		}
		ans=max(ans,x);
	}
	cout<<ans<<endl;
}

2.暴力搜索

因为这道题其实就是求在知到a,b的情况下,求ax+by<m的条件下怎么离m最近,这个时候,我们可以将所以情况遍历,从全是x到全是y一个一个遍历。(丫,我因为long long wa了,哭哭)

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

int n,m;
set<int>st;
map<int,int>mp;
int ans=0;
void find_max(int a,int b) {
	int aa=mp[a];
	int bb=mp[b];
	for(int i=0; i<=aa; i++) {
		int now=i*a;
		if(now>m)
		{
			return ;
		}
		int shen=(m-now)/b;
		ans=max(ans,(now+b*min(bb,shen)));
	}
}

void solve() {
	cin>>n>>m;
	int x;
	st.clear();
	mp.clear();
	ans=0;
	for(int i=0; i<n; i++) {
		cin>>x;
		mp[x]++;
		st.insert(x);
	}
	for(auto it=st.begin(); it!=st.end(); it++) {
		int num=*it;
		int it_num=mp[num];
		ans=max(ans,num*min(m/num,it_num));
		if(mp[num+1]>0) {
			find_max(num,num+1);
		}

	}
	cout<<ans<<"\n";
}

3. 思维

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,m;
ll arr[200005];
map<ll,ll>mp;
ll ans=0;

void find_max(ll a,ll b) {
	if(a>m) {
		return ;
	}
	ll aa=mp[a];
	ll bb=mp[b];
	ll num_a=min(m/a,aa);
	ll count_a=num_a*a;
	ll num_b=min((m-count_a)/b,bb);
	ll count=num_a*a+num_b*b;
	ll shen_num=m-count;
	ll shen_b=bb-num_b;
	ll add=min(min(shen_num,shen_b),num_a);

	ans=max(ans,count+add);

}

void solve() {
	cin>>n>>m;
	mp.clear();
	ans=0;
	for(int i=0; i<n; i++) {
		cin>>arr[i];

	}
	for(int i=0; i<n; i++) {
		cin>>mp[arr[i]];
	}
	sort(arr,arr+n);
	for(int i=0; i<n; i++) {
		ans=max(ans,arr[i]*min(mp[arr[i]],m/arr[i]));
		if(arr[i+1]-arr[i]==1) {
			find_max(arr[i],arr[i+1]);
		}

	}
	cout<<ans<<"\n";
}



int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int q;
	cin >> q;
	while (q--) {
		solve();
	}
	return 0;
}

962的C

题目

在这里插入图片描述
题目链接

思路

1.预处理

这道题要学会奖字母‘a’-'z’转变为0-26,暴力是不行的,所以要预处理,而预处理的思路就是因为字母才26个!!!!,所以开二维是没问题的,不要傻颠颠的想着,开26个数组,二维数组脑子被吃了嘛!

2. 空间复杂度和时间复杂度的理解

先来个正确的代码

对于这段代码我先后经历过好几种空间复杂度与时间复杂度的错误。

  1. 开全局变量时,如果n只能在后面局部输入,全局变量不能用n来代替(报错)
  2. 空间复杂度报错:
    在这里插入图片描述
    上面两行那种会导致空间复杂度的报错,因为这会导致每次都要开2210^5的内存,然后因为t是10 ^5,所以会导致42210 ^510 ^5=16*10 ^10次方比特,除以两个1024为152587mb,超内存了。
    所以将数字换为n类型(下面两行那种)就不会导致mle了,但下面那种不能放到全局变量,所以最好还是把solve1()的内容放回solve(),或者改为vector。
    这种有三种解决方法:1、改为n+5类型。2、改为全局变量。3、改为vector。
  3. 时间复杂度报错:
    在这里插入图片描述
    这个代码中,我是因为放在全局变量,想要初始化,所以用了memset,但TLE了。
    因为memset为O(n),所以当每次去除10 ^5还要10 ^5倍后,会超时。
    要么看后面,1到n都扫过了,直接不初始化,可过。
    要么用vector。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve() {
	string a,b;
	int n,q;
	cin>>n>>q;
	cin>>a>>b;
	vector<array<int,26>>aa(n+2),bb(n+2);
	for(int i=0; i<n; i++) {
		for(int j=0; j<26; j++) {
			aa[i+1][j]=aa[i][j];
			bb[i+1][j]=bb[i][j];
			if((a[i]-'a')==j) {
				aa[i+1][j]++;
			}
			if((b[i]-'a')==j) {
				bb[i+1][j]++;
			}
		}
	}
	while(q--) {
		int l,r,ans=0;
		cin>>l>>r;
		for(int i=0; i<26; i++) {
			int num_a=aa[r][i]-aa[l-1][i];
			int num_b=bb[r][i]-bb[l-1][i];
			//cout<<num_a<<" "<<num_b<<"\n";
			ans+=abs(num_a-num_b);

		}
		if(ans%2==0) {
			cout<<ans/2<<"\n";
		} else {
			cout<<(ans/2)+1<<"\n";
		}
	}
}





int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int T;
	cin >> T;
	while (T--) {
		solve();
	}
	return 0;
}

960的C

题目

在这里插入图片描述
题目链接

思路

这是一道数学思维打表题,找规律。
通过画表能发现,重复两次后,能将所有单个的去掉。此时剩下的数按左上右下的对角线方式相加即可。like:
在这里插入图片描述

注意点:

  1. 开long long
  2. 如果想要accumulate函数来求和,要在后面的0前面加(long long),这里wal好多次!
#include <bits/stdc++.h>
using namespace std;
//using ll=long long;
#define int long long
void solve() {
	int n;
	cin>>n;
	int ans=0;
	vector<int>a(n+1);
	vector<int>b(n+1);

	for(int i=1; i<=n; i++) {
		cin>>a[i];
	}
	a[0]=0;
	int num=0;
	for(int k=0; k<2; k++) {
		int sum=accumulate(a.begin(),a.end(),(long long)0);//这里要(long long!)
		ans+=sum;
		vector<int>v(n+1);
		int c=0;
		for(int i=1; i<=n; i++) {

			v[a[i]]++;
			if(v[a[i]]==2&&a[i]>c)c=a[i];
			b[i]=c;
		}
		for(int i=1; i<=n; i++) {
			a[i]=b[i];
		}


	num=0;

	for(int i=1; i<=n; i++) {
		ans+=a[i]*(n-i+1);
	}
	cout<<ans<<"\n";
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int T;
	cin >> T;
	while (T--) {
		solve();
	}
	return 0;
}

958 C

在这里插入图片描述

题目链接

思路

  1. 会求二进制中为1的位置
cin>>n;
for(int i=0;i<=62;i++)
	{
		if(n>>i&1)
		{
			cout<<i<<"\n";
			//该位置在二进制中为1
		}
	}

2.思路
题目让我们从小到大,但思路可以反过来从大到小,因为n肯定是最大的,我们就可以在二进制位置的0不断往左移(即让数不断变小)【小红书视频】

#include <bits/stdc++.h>
using namespace std;
//using ll = long long;
#define int long long
void solve() {
	int n,num;
	cin>>n;
	set<int>st;
	st.insert(n);
	for(int i=0;i<=62;i++)
	{
		if(n>>i&1)
		{
			num=n^(1LL<<i);
			if(num==0)
			{
				continue;
			}
			st.insert(n^(1LL<<i));
		}
	}
	cout<<st.size()<<"\n";
	for(auto it=st.begin();it!=st.end();it++)
	{
		cout<<(*it)<<" ";
	}
	cout<<"\n";
	
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int t;
	cin >> t;
	while (t--) {
		solve();
	}
	return 0;
}

964的F

题目

在这里插入图片描述

思路

这道题思路其实并不难,难就难在阶乘那里可能会整数溢出。看了大部分的代码,都是利用快速幂和阶乘的模逆元来完成的

#include <bits/stdc++.h>
using namespace std;
//using ll = long long;
#define int long long
const int N=200005;
const int mod=1000000007;
int fac[N],inv[N];
int ans=0;
//快速幂 
int pw(int a, int b) {
	int res = 1;
	while (b) {
		if (b & 1) res = 1ll * res * a % mod;
		a = 1ll * a * a % mod;
		b >>= 1;
	}
	return res;
}
//求阶乘 
int qiu(int n, int k) {
	if (k > n) return 0;
	if(k==0||n==k) {
		return 1;
	}
	return fac[n] * inv[k] % mod * inv[n - k] % mod;
}

void solve() {
	int n,k,x,ans=0;
	cin>>n>>k;
	int mid=k/2+1;
	int sum1=0,sum0=0;
	for (int i=0; i<n; i++) {
		cin>>x;
		if(x==1) {
			sum1++;
		} else {
			sum0++;
		}
	}
	mid=max(mid,k-sum0);
	while(mid<=sum1&&mid<=k) {
		int qiu1=qiu(sum1,mid);
		int qiu2=qiu(sum0,k-mid);
		//cout<<"mid"<<mid<<" "<<qiu1<<" "<<qiu2<<"\n";
		ans+=(qiu1*qiu2)%mod;
		mid++;
	}
	cout<<ans%mod<<"\n";

}


signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int t;
	cin>>t;
	fac[0] = inv[0] = 1;
	for (int i = 1; i < N; i++) {
		fac[i] = 1ll * fac[i - 1] * i % mod;
		//求模逆元 
		inv[i] = pw(fac[i], mod - 2);
	}
	while(t--) {
		solve();
	}
	return 0;
}

964的G2

题目

在这里插入图片描述

思路

这道题的思路就是学会交互和三分

#include <bits/stdc++.h>
using namespace std;
//using ll = long long;
#define int long long
const int N=200005;
void solve() {
	int l=1, r=1000,res;
	while(l+2<r) {
		int mid1=l+(r-l)/3;
		int mid2=l+((r-l)/3)*2;
		cout<<"? "<<mid1<<" "<<mid2<<"\n";
		cin>>res;
		if(res==(mid1*mid2)) {
			l=mid2+1;
		} else if(res==((mid2+1)*mid1)) {

			l=mid1+1;
			r=mid2;
		} else {
			r=mid1;
		}
		cout.flush();
	}
	if(l+1==r) {
		cout<<"? "<<l<<" "<<r<<"\n";
		cin>>res;
		if(res==(l+1)*(r+1)) {
			cout<<"! "<<l<<"\n";
		} else {
			cout<<"! "<<r<<"\n";
		}
	} else {
		cout<<"? "<<l<<" "<<l+1<<"\n";
		cin>>res;
		if(res==(l+1)*(l+2)) {
			cout<<"! "<<l<<"\n";
		} else if(res==l*(l+1)) {
			cout<<"! "<<r<<"\n";
		} else {
			cout<<"! "<<l+1<<"\n";
		}
	}

	cout.flush();
}


signed main() {
//	ios::sync_with_stdio(false);
//	cin.tie(nullptr);
	int t;
	cin>>t;
	while(t--) {
		solve();
	}
	return 0;
}

963的c

题目

在这里插入图片描述

思路

这道题的思路为周期+差分题目,可以将所有数的聚集在max后的2k中,如果在这2k的期间内不行,那就是不行。
然后思路就是,开灯+1,关灯-1,当isopen==n的时候,就说明灯都打开了。(差分思路)

#include <bits/stdc++.h>
using namespace std;
//using ll = long long;
#define int long long
const int N=200005;
void solve() {
	int n,k,maxn=0,isopen=0,num,yu;
	cin>>n>>k;
	int a[n+2];
	int open[2*k+1]={0};
	for(int i=0; i<n; i++) {
		cin>>a[i];
		maxn=max(maxn,a[i]);
	}
	for(int i=0; i<n; i++) {
		num=maxn-a[i];
		yu=num%k;
		if((num/k)%2==0) {
			isopen++;
			open[k-yu]--;
			open[2*k-yu]++;
		} else {
			open[k-yu]++;
			open[2*k-yu]--;
		}
	}
	for(int i=0;i<=2*k;i++){
//		cout<<"open"<<isopen<<" "<<open[i]<<"\n";
		isopen+=open[i];
		if(isopen==n)
		{
			cout<<maxn+i<<"\n";
			return;
		 } 
	}
	cout<<-1<<"\n";
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int t;
	cin>>t;
	while(t--) {
		solve();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值