Codeforces Round 905 (Div. 2)补题

Chemistry

题目大意:给一个长为n的字符串s,问能否删除k个字符,然后通过排序得到一个回文串。

思路:首先看回文串,分奇偶两种情况,奇数长时,有且仅有一个字符的数量是奇数个,偶数长时,所有字符的个数都必须是偶数。那么我们来统计每个字符的个数,若有m个奇数个数的字符,我们比较m和k的关系,如果m-k>1,那么一定判否,如果m-k==1||m-k==0,那么是ok的;如果m-k<0,那么我们先将k=k-m,然后,此时所有字符的个数都是偶数个,k奇偶就没什么影响。

#include<bits/stdc++.h>
using namespace std;
map<char,int>mp;
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n,k;
		scanf("%d%d",&n,&k);
		string s;
		cin>>s;
		for(int i=0;i<s.size();i++)
		{
			mp[s[i]]++;
		}
		int j=0;
		for(auto it:mp)
		{
			if(it.second%2) j++;
		}
		if(j-k>=2) printf("NO\n");
		else printf("YES\n");
		mp.clear();
	}
}

Raspberries

题目大意:给定一个数组a[]和一个整数k,我们每次操作可以选择一个a[i],使a[i]=a[i]+1;问要使a[1]*a[2]*...*a[n]%k==0,至少要操作多少次。

思路:所有数的乘积能够整除k,那么实际上只要有一个数字能够整除k即可。那么我们对于每个数都mod k,如果然后它需要加上的数为min(a[i]%k,k-a[i]%k),再从里面找最小的即可。对了,还要注意到一个情况,如果k等4,是可以由两个2相乘得到的。不过k的范围在2-5之间,比较好讨论,我们对4单独处理,除了记录余数之外同时记录数组中偶数的个数,然后0个偶数,ans=min(2,mi),1个偶数ans=min(1,mi);大于两个偶数的时候,直接输出0.

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n,k;
		scanf("%d%d",&n,&k);
		if(k!=4)
		{
			int mi=1e9+7;
			for(int i=0;i<n;i++)
			{
				int x;
				scanf("%d",&x);
				x %= k;
				if(x!=0) x=k-x;
				mi = min(mi,x);
			}
			printf("%d\n",mi);
		}
		else
		{
			int o=0,flag=1;
			int mi=1e9+7;
			for(int i=0;i<n;i++)
			{
				int x;
				scanf("%d",&x);
				if(x%2==0) o++;
				x %= k;
				if(x!=0) x=k-x;
				mi = min(mi,x);
			}
			if(o==0) mi=min(2,mi),printf("%d\n",mi);
			else if(o==1) mi=min(1,mi),printf("%d\n",mi);
			else mi=min(mi,0),printf("%d\n",mi);
		}
	}
}

You Are So Beautiful

题目大意:我们给定一个长n数组a[],现在定义它的子数组为b[],我们要找出b[]作为子序列仅出现一次的有多少个。

思路:我们以2 3 2 1作为例子来考虑,可以发现,2 3可以但是2 1不可以,所以实际上就是对于一个数字,只能和第一次出现和第二次出现之间的数构成子数组,因为一旦跟后面的构成了,那么前面的一定可以替换它,产生相同的新的子序列,如果仅出现一次,那么与它后面的所有的构成的子数组都是可以的吗?实际上不是,也得是最后一次出现的位置才行,不然右边就有其他的能代替该子数组的末尾元素产生新的相同的子数组。所以我们得到的子数组的首元素必须是在数组中第一次出现的位置,末尾元素一定是最后一次在数组中出现的位置。对于子数组,首末位置定了,那么就相当于定了子数组。我们只用从前往后统计出第一次出现位置,从后往前找出最后一次出现的位置,然后匹配即可。匹配的时候如果直接循环,是会超时的,那么我们就要优化查找的过程,这里我是用二分来优化的:

for(auto i:p1)
{
	int l=0,r=p2.size()-1;
	//找等于,实际上也是在找最后一个可以取的位置
	while(l<r)
	{
		int mid=(l+r+1)/2;
		if(p2[mid]>=i) l=mid;
		else r=mid-1;
	}
    //l取的要么是满足条件的,即p2[l]>=i,要么是0,此时所有的都不满足
	if(p2[l]<i) ans =ans;//有可能一个都不能取,即此时l==0,但是p2[l]<i
	else ans += l+1;
}

完整代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[100010];
signed main()
{
	int t;
	scanf("%lld",&t);
	while(t--)
	{
		map<int,int>mp1,mp2;
		vector<int>p1,p2;
		int n;
		scanf("%lld",&n);
		for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
		for(int i=1;i<=n;i++)
		{
			if(!mp1.count(a[i])) mp1[a[i]]=i,p1.push_back(i);
		}
		for(int i=n;i>=1;i--)
		{
			if(!mp2.count(a[i])) mp2[a[i]]=i,p2.push_back(i);
		}
		int ans=0;
		for(auto i:p1)
		{
			int l=0,r=p2.size()-1;
			//找等于
			while(l<r)
			{
				int mid=(l+r+1)/2;
				if(p2[mid]>=i) l=mid;
				else r=mid-1;
			}
			if(p2[l]<i) ans =ans;
			else ans += l+1;
		}
		printf("%lld\n",ans);
	}
}

Dances (Easy version)

题目大意:我们给定a[]和b[],每次操作前都可以进行排序,操作:

从a[]中选一个元素并删除,从b[]中选一个元素并删除。

假设最后剩余k个元素,求满足1<=i<=k,ai<bi的最小操作数。

这样来看题目太简单了,我们引入一个变量m,不过对于本题m=1,我们给a[]={a2,a3,...,an},b[]={b1,b2,...,bn};最后要得到的是(c[i],b[]),c来自a[],按照如下规则生成:
c[i]1=i,i=1;

c[i]j=a[j];

(1<=i<=m,本题比较简单,只有一个i值,只用考虑一组情况。)

我们要求对于所有的(c[i],b[])的最小操作数。

思路:本题限定了m=1,比较简单。我们首先要移出的是b中所有的1,很明显c[]的第一个元素是1,那么就是说,b中最小的元素不能是1;另外a[]中所有大于等于max(b)的元素也要移出,然后两者的操作数取最大,b从前面移出若干元素,a从后面移出若干元素。然后我们其实也不用真的移出,我们只用限定好双指针的范围即可,每次移出的时候,将当前不符合要求的a[i]移出,同时移除b[]的开头元素,至此问题解决。

#include<bits/stdc++.h>
using namespace std;
int a[100010],b[100010];
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n,m;
		scanf("%d%d",&n,&m);
		for(int i=2;i<=n;i++) scanf("%d",&a[i]);
		for(int i=1;i<=n;i++) scanf("%d",&b[i]);
		sort(a+2,a+1+n);
		sort(b+1,b+1+n);
		int x1=0;
		for(int i=1;i<=n;i++)
		{
			if(b[i]==1) x1++;
		}
		int x2=0;
		for(int i=2;i<=n;i++)
		{
			if(a[i]>=b[n]) x2++;
		}
		int mx=max(x1,x2);
		int as=2,bs=1+mx;
		int i=n-mx,j=n;
		while(i>=as&&j>=bs)
		{
			if(a[i]<b[j]) 
			{
				i--,j--;
				continue;
			}
			else //删掉a[i]和b的开头
			{
				mx++;
				i--;
				bs++;
			}
		}
		printf("%d\n",mx);
	}
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值