杂题总结(主CF)

Move Back at a Cost

难度不高,容易有思路,但是不一定能想出正解。
首先容易想到结果一定是单调不降的,同时我们大体的思路是找到最值把它扔到最前面。(这一部分是自己思考的)。然后,深入研究(看题解)可知我们操作的顺序是有用的,我们将要加一的数放到最后的时候按从小到大排是最好的。所以一次操作后,数列一定是单调不降的两个序列,但是因为第二个的开头不一定比第一个的结尾高,所以还要调整大于第二开头加一的数。然后我们发现这两轮需要操作的数可以一起处理,按一定的顺序扔到后面即可。所以找两个最小值,确定修改的数,再排序即可。

#include<bits/stdc++.h>
using namespace std;
int t,n,a[100005],mn,emn,f[100005];
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>t;
	while(t--)
	{
		cin>>n;
		mn=emn=2e9;
		for(int i=1;i<=n;i++){cin>>a[i];f[i]=0;}
		for(int i=n;i>=1;i--)
		{
			if(mn>a[i])mn=a[i];
			if(a[i]>mn)
			{
				a[i]++;f[i]=1;
				emn=min(emn,a[i]);
			}
		}
		for(int i=n;i>=1;i--)if(f[i]==0&&a[i]>emn)a[i]++;//第二轮寻找A数
		sort(a+1,a+n+1);
		for(int i=1;i<=n;i++)cout<<a[i]<<" ";
		cout<<"\n";
	}
	return 0;
}

CF2057C Trip to the Olympiad

基本是自己做出来的。
首先我们一定是想要某一位有一个或两个一,这样这一位可以贡献 2(1<<i) 。但是由于有 l,r 的限制,所以要将 l,r相同的前缀加上,然后剩下的每个位置分给 a,b。最简单的就是0111111...10000....(r这一位一定为1,l这一位一定为0),cl,ra,b 不同的任意一个,因为每一位一定与其中一个相同,与另一个不同。同时不可能 a,b 就是 l,r,因为rl>1。所以就可以了。

#include <bits/stdc++.h>
using namespace std;
int t,l,r;
int getbit(int n,int i){return n&(1<<i);}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>t;
	while(t--)
	{
		cin>>l>>r;
		bool flag=0;int now=0; 
		for(int i=29;i>=0;i--)
		{
			if(getbit(l,i)!=getbit(r,i))//比较任意这一位是否相同 
			{
				int x=(1<<i)-1,y=(1<<i);//构建x:011111..y:10000.... 
				x+=now,y+=now;//加上前缀
				cout<<x<<' '<<y<<' ';
				if(x==l||y==l)cout<<r<<'\n';
				else cout<<l<<'\n'; 
				flag=1;//标记 
			}
			if(flag)break;
			now+=(getbit(l,i))?(1<<i):0; //前缀 
		//	cout<<i<<' '<<getbit(l,i)<<' '<<now<<'\n';
		}
	}
	return 0;
}

CF1997C Even Positions

直接做出来的简单题。
读完题面就发现如果一个地方可以放右括号,就一定要放右括号,把右括号往后放一定是不优的(因为答案是右括号之和减左括号之和最小)。所以就记录一下前面有多少个左括号和右括号,能填就填右。
但是我们需要证明一下,按这样的规则是否合法,我们发现一定是合法的,不可能出现右括号比左括号多的情况(因为所有奇数被挖空)。
拓展:
如果去掉奇数和偶数的限制,规定一定的位置填左括号和右括号,是否有好做法?(还在思考)

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int t,n;
char s[N];
int main()
{
	cin>>t;
	for(int i=1;i<=t;i++)
	{
		cin>>n>>s+1;
		int zk=0,yk=0,ans=0;
		for(int j=1;j<=n;j++)
		{
			if(j&1)//奇数 
			{
				if(zk<=yk)zk++,ans-=j; 
				else yk++,ans+=j;
			}
			else 
			{
				if(s[j]=='(')zk++,ans-=j;
				else yk++,ans+=j;
			}
		}
		cout<<ans<<'\n';
	}
	return 0;
} 

CF2004C Splitting Items

自己做出来的简单题,只要转换一下即可。
首先可以发现最优策略是从大到小排列,A 取奇数上的数,B 取偶数上的数。然后我们只需要将偶数上的数尽量补到与前一个数相同即可。
本来已经做完了,结果我为了简便,将偶数上的加起来,奇数上的加起来,结果为max(偶-奇-k,0),错了。(因为没有考虑边界情况,有可能 n 是奇数)

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int t,n,k,a[N];
bool cmp(int x,int y){return x>y;}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>t;
	while(t--)
	{
	cin>>n>>k;long long suma=0,sumb=0;
	for(int i=1;i<=n;i++)cin>>a[i];
	sort(a+1,a+n+1,cmp);
	for(int i=2;i<=n;i+=2)
	{
		if(k>=a[i-1]-a[i])
		{
			k-=a[i-1]-a[i];
			a[i]=a[i-1];
		}
		else 
		{
			a[i]+=k;
			break; 
		}
	}
	for(int i=1;i<=n;i+=2)suma+=a[i];
	for(int i=2;i<=n;i+=2)sumb+=a[i];
	cout<<suma-sumb<<'\n';
	}
	

CF1967A Permutation Counting

自己做出来的简单题。
显然我们应该按a1,a2,a3...,a1,a2,..的顺序排(注意最优的应该把数量最小的放在最后),所以影响我们的应该是最小的数量。
所以问题转化为最小值最大的问题,所以二分解决问题,即可。
拓展:还有实现更为复杂的线性做法,不过总时间复杂度瓶颈不在最后,所以时间复杂度不变。

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
#define ll long long
const ll ee=1e18+10;
ll ans,t,n,k,a[N];
ll check(ll x)
{
	ll cnt=0,sum=0;
	for(int i=1;i<=n;i++)
	{
		if(a[i]<=x)cnt++,sum+=x-a[i];
		if(sum>k)return ee; 
	}
	return cnt-(k-sum);
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>t;
	while(t--)
	{
		cin>>n>>k;
		for(int i=1;i<=n;i++)cin>>a[i];
		ll l=1,r=1e15+10,mid,sum=0;
		while(l<=r)
		{
			mid=(l+r)>>1;ll tmp=check(mid);
			if(tmp!=ee)ans=mid,sum=tmp,l=mid+1;
			else r=mid-1;
		}
		cout<<ans*n-(n-1)+n-sum<<'\n';
	}
	return 0;
} 

CF1312D Count the Arrays

一眼题。
很显然分界点一定是且相同的数一定不是最大值 n,剩下 n2 个数有一个数可以都选方案是(n2)。然后考虑那些数放左边,那些数放右边,方案为i=0n3(n3i)=2n3(要保证相同的数一左一右)。
所以总的方案数是 (mn1)×(n2)×2n3 直接做即可。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+10,mod=998244353;
int n,m;
int jc[N],inv[N],ans;
int ksm(int x,int y)
{
	int res=1;
	while(y)
	{
		if(y&1)res=res*x%mod;
		y>>=1;
		x=x*x%mod;
	}
	return res;
 } 
signed main()
{
	cin>>n>>m;
	jc[0]=inv[0]=1;
	for(int i=1;i<=m;i++)jc[i]=jc[i-1]*i%mod;
	inv[m]=ksm(jc[m],mod-2);
	for(int i=m-1;i>=1;i--)inv[i]=inv[i+1]*(i+1)%mod;
	if(n==2)
	{
		cout<<"0"<<'\n';
		return 0;
	}
	int es=ksm(2,n-3);
	cout<<(n-2)%mod*jc[m]%mod*inv[n-1]%mod*inv[m-n+1]%mod*es%mod<<'\n';
	return 0;
} 

CF1420D Rescue Nibel!

CF1288C Two Arrays

最开始想用dp,设dpi,a1,b1 表示处理到第 i 位,a 上是 a1,b 上是 b1 的方案数,转移是改造后的前缀和,复杂度是 O(n2m),感觉非常烂,又不会优化。
然后,发现感觉可以直接用组合数写式子。枚举 a 的最大值和 b 的最小值,就可以理解有 m 个相同的球(位置),i 个不同的盒子可以为空,求方案数,用插板法计算即可。化简式子后前缀和可以 O(n+m) 计算。
最后发现上面的 dp 可以改进,我们可以发现 a,b 是很像是的只是一个是不降,一个是不升。我们考虑可以将 b 倒转可以和 a 拼起来,这样整个序列就成了一个不降(我没想到的地方),可以O(nm) 做出来。

code
#include <bits/stdc++.h>
using namespace std;
const int N=1010,mod=1e9+7;
int n,m,sum[30][N],dp[30][N];
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)sum[0][i]=1; 
	for(int i=1;i<=2*m;i++)
	{
		for(int j=1;j<=n;j++)
			dp[i][j]=sum[i-1][j];
		for(int j=1;j<=n;j++)
			sum[i][j]=(sum[i][j-1]+dp[i][j])%mod;
	}
	cout<<sum[2*m][n]<<'\n';
	return 0;
}
posted @   storms11  阅读(16)  评论(4编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示