CF1909题解

CF1909A

一眼秒之题,我们发现就是四个方向选三个方向,若是存在一个点它的方向恰好在(0,0)点的另外一个方向,则一定不成立

枚举4个方向,发现有点在这个方向,显然选除这个点之外的三个方向的方案就不可行

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=105;
int t,n;
int x[N],y[N];
bool c1(){
	for(int i=1;i<=n;i++){
		if(x[i]<0)  return 0;
	}
	return 1;
}
bool c2(){
	for(int i=1;i<=n;i++){
		if(x[i]>0)  return 0;
	}
	return 1;
}
bool c3(){
	for(int i=1;i<=n;i++){
		if(y[i]<0)  return 0;
	}
	return 1;
}
bool c4(){
	for(int i=1;i<=n;i++){
		if(y[i]>0)  return 0;
	}
	return 1;
}
int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		for(int i=1;i<=n;i++){
			scanf("%d%d",&x[i],&y[i]);
		}
		if(c1()||c2()||c3()||c4())  printf("YES\n");
		else  printf("NO\n");
	}
}

CF1909B

看似普及-,实则封神

我们先想到若这些数有奇数也有偶数k直接等于2即可

然而若不是呢?

判断一个数的奇偶只需要看它的二进制最低位是0/1就行

若一个数列全是奇或偶那么必然二进制第一位上全是1或全是0

但是其必然会出现在二进制的某一位上1/0两种都存在

若一个数模上一个 \(2^k\) 那么在2进制下只有 1~k+1位上数是有效的

所以我们只需要找到在二进制表示的数列中,找到最低的一位满足这一位存在0/1即可

这显然可以字典树做,但这道题普及-所以我们暴力枚举 \(2^i\) 即可

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=105,inf=1e18;
int t,n;
int a[N];
bool check(int mod){
	set<int>st;
	for(int i=1;i<=n;i++){
		int z=a[i]%mod;
		st.insert(z);
	}
	if(st.size()!=2)  return 0;
	return 1;
}
signed main(){
	scanf("%lld",&t);
	while(t--){
		int ans=inf;
		scanf("%lld",&n);
		for(int i=1;i<=n;i++){
			scanf("%lld",&a[i]);
		}
		for(int j=0;j<=63;j++){
			int mod=(1ll<<j);
			if(check(mod)){
				printf("%lld\n",mod);
				break;
			}
		}
	}
}

CF19909C

小恶心贪心题

显然我们是要求出来一个降序序列 \(r_i-l_i\) 使得其每一位乘上升序排序的 \(c_i\) 得出来的结果最小

考虑如何构造出对应的 \(l_i\)\(r_i\) 呢?

我们先将 \(l,r\) 序列排好序,然后考虑以下两种组合情况

所以我们只需要让在 \(l_i<r_i\) 这个限制范围内使得 \(l_i\) 尽量接近 \(r_i\),我们可以用栈来解决这个问题

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int n,cnt,t;
int l[N],r[N],c[N],s[N],ans[N];
bool cmp(int x,int y){
	return x>y;
}
signed main(){
	scanf("%lld",&t);
	while(t--){
		cnt=0;
		scanf("%lld",&n);
		for(int i=1;i<=n;i++){
			scanf("%lld",&l[i]);
		}
		for(int i=1;i<=n;i++){
			scanf("%lld",&r[i]);
		}
		for(int i=1;i<=n;i++){
			scanf("%lld",&c[i]);
		}
		sort(c+1,c+1+n);
		sort(l+1,l+1+n);
		sort(r+1,r+1+n);
		for(int i=1,j=1;i<=n;i++){
			while(l[j]<r[i]&&j<=n){
				s[++cnt]=l[j];
				j++;
			}
			// printf("%lld %lld\n",r[i],s[cnt]);
			ans[i]=r[i]-s[cnt--];
		}
		sort(ans+1,ans+1+n,cmp);
		int tot=0;
		for(int i=1;i<=n;i++){
			tot+=ans[i]*c[i];
		}
		printf("%lld\n",tot);
	}
}

CF1909D

呵呵,又是一道恶心题

考虑转化一下式子

\(x+k=y+z\) -> \((x-k)=(y-k)+(z-k)\)

所以我们就将所有的 \(a[i]-k\),然后题目就转化成了抹掉一个数 \(x\),替换成 \(y,z\),满足 \(x=y+z\),求最小把这个序列变成同一个数的最小操作次数

考虑不管怎么拆分这个序列,这个序列的总和 \(sum\) 是不变的,因为最后数列要拆成所有数都相同的形式,我们设这个数为 \(k\),显然 \(sum=tk\),最终答案即为 \(t-n\)

如何求这个 \(k\) 呢,考虑对于 \(a[i]\) 的拆法,它怎么拆是独立的并不受两边数的影响,因为它拆出来的数一定是相等的,所以可以证明 \(a[i]=mk\) (k是拆出来的数)中 \(k\)\(a[i]\) 的因数

我们现在证明了 \(k\) 能被 \(a[i]\) 拆出来当且仅当 \(k\)\(a[i]\) 的因数,如何证明这是充要条件?

考虑一种拆法,\(k\)\(a[i]\) 的因数,固有 \(a[i]=mk\),我们先把 \(a[i]\) 拆成 \([(m-1)k,k]\),然后再 \([(m-2)k,k,k]\) ······ 依此类推。

所以最终我们就是要找到序列 \(a\) 的最大公因数,即可求得答案

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+5;
int t,n,k;
int a[N];
bool check1(){
	for(int i=2;i<=n;i++){
		if(a[i]!=a[i-1])  return 0;
	}
	return 1;
}
bool check2(){
	if(a[1]==0)  return 1;
	int z=(a[1]>0);
	for(int i=2;i<=n;i++){
		if(a[i]==0)  return 1;
		if((a[i]>0)!=z)  return 1;
	}
	return 0;
}
int gcd(int x,int y){
	if(!y)  return x;
	return gcd(y,x%y);
}
signed main(){
	scanf("%lld",&t);
	while(t--){
		scanf("%lld%lld",&n,&k);
		for(int i=1;i<=n;i++){
			scanf("%lld",&a[i]);
			a[i]-=k;
		}
		if(check1()){
			printf("0\n");
			continue;
		}
		if(check2()){
			printf("-1\n");
			continue;
		}
		int num=0,tot=0;
		tot+=a[1];
		num=a[1];
		for(int i=2;i<=n;i++){
			tot+=a[i];
			num=gcd(num,a[i]);
		}
		printf("%lld\n",tot/num-n);
	}
}

CF1909E

题解讲的比我好(bushi

posted @ 2024-11-06 15:39  daydreamer_zcxnb  阅读(169)  评论(0编辑  收藏  举报