Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备)

A. Ancient Civilization

题目传送门


题意:给你一个数组a(十进制),长度为n,Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备),现在要求你拿出一个ans(十进制),Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备),使得这a[i](二进制)与ans(二进制)对应位置差异度和最小。

举个栗子:a[i]=5的二进制是101,ans=10的二进制是1010,咱们从右往左看,发现第一位,第二位,第三位,第四位都不一样,现在的差异度就是4,而差异度和就是Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备)

思路:这题的难点就是看懂题(嘿嘿,看懂题应该大家就会了吧),那其实是这就是一个选择问题,ans(二进制)位数最多就是 l 位,我们只需要考虑当前第 i 位取与不取,为了让差异度最小,那么我们pre数组存所有数(二进制)第 i 位的 1 的个数,然后,若当前第 i 位的 1 的个数大于了Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备)就取(一半的时候随便取不取,我这里没有取)

核心代码:

if(pre[i]>n-pre[i])b[i]=1;//b[]数组用来存ans的二进制

快速幂:

int kuai(int s,int n){
	int ans=1;
	while(n){
		if(n&1)ans*=s;
		s*=s;
		n>>=1;
	}
	return ans;
}

快读:

inline int read() {
    char ch = getchar(); int x = 0, f = 1;
    while(ch < '0' || ch > '9') {
        if(ch == '-') f = -1;
        ch = getchar();
    } while('0' <= ch && ch <= '9') {
        x = x * 10 + ch - '0';
        ch = getchar();
    } return x * f;
}

而要实现这一切我们还得有一个十进制转换二进制(都2022年了博主还是只能手搓),和最后快速幂(当然可以用pow,但是pow是double类型的,出了问题你想不到)求ans答案,那么话说了这么多,上代码

t=read();//t组数据,这里博主用了快读,减少时间
while(t--){
	n=read();l=read();//n个数,l是二进制最大位数
	for(i=1;i<=n;i++)a[i]=read();
	for(i=1;i<=n;i++){
		da1=a[i];sum=1;        //
		while(da1){            //手搓十进制转二进制
			if(da1%2){         //
				pre[sum]++;    //
			}                  //
			da1>>=1;           //
			sum++;             //
		}
	}
	for(i=1;i<=l;i++)if(pre[i]>n-pre[i])b[i]=1;//计算ans(二进制)
	ans=0;//可别忘记更新ans哦,t组数据
	for(i=1;i<=l;i++)if(b[i])ans+=kuai(2,i-1);//手搓二进制转十进制,kuai(2,i-1)是快速幂这里博主就不放出来了
	cout<<ans<<endl;
	for(i=1;i<=l;i++)pre[i]=0;//别忘记了跟新pre数组状态
}
return 0;

B. Elementary Particles

题目传送门


题意:t组数据,给你一个长度为n的数组a,现在要求你在数组中截取两个不相同但是长度相等的子序列,唯一要求是使得至少存在一个对应的位置上面数字一样,求最长长度是多少(不存在则输出-1)

Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备)

 

比如1 2 3和5 2 7就是满足条件的,因为他们第二个位置相等,而1 3 2和2 1 3就不满足条件

思路:既然这是一道存在性的题目,那么偷奸耍滑的我们肯定直接考虑只要有一个相等的存在即可,现在我们给你一个a数组Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备)此时若Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备)Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备)相等,那么此时由他们构成的数列最大是多少呢?是Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备)Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备)。我们发现只要Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备)的左边序列的长度,和Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备)右边序列的长度,为什么这么说呢?因为Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备)Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备)的左边,因此Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备)左边能取到多长,Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备)一定能取到,所以我们看Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备)的左边,同理,这也是我们只看Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备)右边的理由

关键步骤:因此我们只需要利用sort进行结构体排序(利用结构题,在存下每个数的时候,将它所在的位置也存下来)

关键公式:我们现在令Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备)的位置为 l ,令Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备)的位置为 r ,则长度Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备),因为n为已知量,所以我们只需要考虑 l - r 最小,又因为我们结构体排序过了,所以我们只要需要算相邻两个值的差绝对值最小的那一个(前提是这两个数的值相等),最后和n相加即可

关键代码:

cmp:

bool cmp(node x,node y)
{
	if(x.da==y.da)return x.xu<y.xu;//其实这步有些多余,不过为了安心还是写上去了,因为如何一样cmp是不会继续排下去的 
	return x.da<y.da;
}

 对啦,写的时候就利用尺取的思想,l,r一起动就好啦

上代码:

struct node{
	int xu,da;//xu是位置,da则是值 
}a[500005];
bool cmp(node x,node y)
{
	if(x.da==y.da)return x.xu<y.xu;//其实这步有些多余,不过为了安心还是写上去了,因为如何一样cmp是不会继续排下去的 
	return x.da<y.da;
}
signed main()
{
	int i,j,t;int n,l,r,sum;//n个数,t组数据,l,r当前排过序后数列的位置,sum则是值 
//	jian1;jian2;jian3;//l,r位置的解读,例如现在1,8,3,现在l为2则在当前数列的位置是2
	t=read();//快读 
	while(t--){
		n=read();
		for(i=1;i<=n;i++){
			a[i].da=read();
			a[i].xu=i;//存当前数的位置 
		}
		sort(a+1,a+1+n,cmp);//排序 
		sum=-1;l=1;r=2;//l一定是小于r,当然你想要让他等于改一下后面就好了
		while(r<=n){//r可不能出界哦(⊙o⊙),一共就n个数诶 
			if(a[l].da==a[r].da)sum=max(sum,a[l].xu+n-a[r].xu);//毕竟最后的答案可是求最值,所以得比较比较 
			l++;r++;//l当前位置的数被用过了 ,已经没了价值所以l++ 
		}
		cout<<sum<<endl;//输出答案 
	}
	return 0;
}

C. Road Optimization

题目传送门

题意:一条路长度为len,n给限速标(第一个限速标一定再起点上面),用长度为n数组a表示的坐标(单位是公里),再给一个与数组a对应的b数组,记录第 i 个标到第 i+1 个标类每公里所需要的时间,算法为:Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备),现在允许你最多删除k个点(也可以不删(⊙.⊙)),求Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备)最小

思路:此题看一眼,嗯...发现没有思路,看第二眼,嗯...没有思路,摆了摆了

其实此题应该用动态规划,局部最优使全局最优(前提是没有后效性,即前面你无论怎么选都不会影响到后面的选择)因此我写出Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备)意思是指保留第 i 个限速标(即不删除),然后现在还有p个删除的名额(即还可以删除p个限速标),现在关键是dp方程如何写

关键代码:

dp方程:Codeforces Round #765 (Div. 2) A~C 题解(原来如此简单,超级详细入门必备)

这里我就先把dp方程给了出来,里面的 j 是什么呢,里面的 j 是从第 j 个到第 i 个之内的限速标q全部删除(不包括 i,j ),对我们令最后的终点也是一个点,即a[n+1]=len

这里你可能会问,因为是连着删除不会漏解吗?当然不会,现在解惑:

首先我们的第 i 个保留,这就使得我们解决了后效性的问题,因为无论前面怎么删除,就算捅破了天,都不会影响后面的删除,其次,j 是小于 i 的,是指从第 j 个到第 i 个之内的限速标q全部删除(不包括 i,j ),所以我们是步步最优

废话不多说,直接上代码:

signed main()
{
	int i,j,t;int n,len,p,k;
	n=read();
	len=read();
	k=read();
	for(i=1;i<=n;i++)a[i]=read();
	for(i=1;i<=n;i++)b[i]=read();
	a[++n]=len;
	for(i=1;i<=n;i++)for(j=0;j<=k;j++)dp[i][j]=1e15;//我们得先将dp数组初始话,毕竟我们是一步一步去比较最小值,所以我们初始化为最大值 
	dp[1][k]=0;
	for(i=2;i<=n;i++)
		for(j=1;j<i;j++)
			for(p=0;p<=k;p++)//要使用dp方程首先要保证咱们现在删除的路标没有超出k个,i和j之间有i-j-1个 
				if(p+i-j-1<=k)dp[i][p]=min(dp[i][p],dp[j][p+i-j-1]+(a[i]-a[j])*b[j]);
	cout<<*min_element(&dp[n][0],&dp[n][k+1])<<endl;//因为答案是一步一步更新上来的,所以我们只需要找到在n处,删除0-k个的最小值 
	return 0;
}

由于博主太菜,当时没有写出来,赛后补题++

希望大家看了这篇文章都有进步,加油!(⊙0⊙)!

对了推一个知乎上面大佬的题解Codeforces Round #765 (Div. 2) A~D - 知乎 (zhihu.com)

博主的dp就是和这个大佬学的,主要是大佬讲的有点深奥,博主理解了好久

上一篇:Codeforces Round #765 (Div. 2)


下一篇:Codeforces Round #764 (Div. 3) G题