一些CF上的补题0504

知识点模块

1.通过三点计算三角形的面积可以这样写
area=fabs(x1*y2-x2*y1+x2*y3-x3*y2+x3*y1-x1*y3)/2;
2.最小公倍数与最大公约数
x×y=gcd(x,y)*lcm(x,y)
3.预处理非最小值每个点下一个的最小值的位置
for(int i=n,p=1e9;i>=1;i--)
{pos[i]=p;
if(ve[i]==minn) p=i;}

4.初始化一个有30位个1的二进制位
int a=(1<<30)-1

B. AND Sorting
这题我们发现将样例中的每个位置不匹配的按顺序与下去,得到的就是结果,猜一猜写一下,学习一下使用二进制数1111111111.....的初始化

点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
void solve()
{
	int n;
	cin>>n;
	int ans= (1<<30) -1;//初始化一个很长的111111111 
	//其实他们的解释我也听不懂
	//但是反正每个位置不同的数与一下就是样例的结果
	//猜着猜着来吧 
	for(int i=0;i<n;i++)
	{
		int x;
		cin>>x;
		if(x!=i) ans&=x;
	}
	cout<<ans<<endl;
}


signed main()
{
  	int t=1;
  	cin>>t;
  	while(t--) solve();
  	return 0;
}

C. 3SUM Closure
1.假如有n个数,里面至少有3个数a>b>c>0,那么a+b+c>a是肯定的,这个元素肯定不符合题意,那么三个负数也是同理的。
2.我们注意到当三个数为0 0 x的时候,无论你的x是任何数,都是符合题意的,那么如果有很多个0和x,只需要取1个0,怎么理解呢,如果我们是-2,2,0,0其实和-2 2 0是等价的,举其他例子也是同理。
3.综上,我们其实最多只能取到两个正数,两个负数和一个0的情况,为什么一定要有1个0呢?因为我举不出来没有0可以符合的情况。
所以我们只需要检查一下,有没有0,检查一下有几个正数负数,检查完以后再暴力遍历验证一下就可以

点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
void solve()
{
	int n,markz=0,markf=0,zero=0,check=1;
	cin>>n;
	vector<int>ve;
	map<int,int>mp;
	for(int i=1;i<=n;i++)
	{
		int x;
		cin>>x;
		if(x>0) {
			if(++markz<3) ve.push_back(x),mp[x]=1,check=0;
		}
		else if(x<0)
		{
			if(++markf<3) ve.push_back(x),mp[x]=1,check=0;
		}
		else if(++zero==1) ve.push_back(x),mp[x]=1;
	}
	//如果有三个及以上正数或者三个及以上负数都不符合
	//因为a+b+c>a(a最大) 负数同理 
	//由于0 0 x当x取任何数都符合情况,所以当有多个0的时候,我们只需要把一个放入数组即可 
	if(markz>=3||markf>=3) {
		cout<<"NO"<<endl;
		return ;
	}
	if(check==1) {
		cout<<"YES"<<endl;
		return;
	}
	for(int i=0;i<ve.size();i++)
	{
		for(int j=i+1;j<ve.size();j++)
		{
			for(int k=j+1;k<ve.size();k++)
			{
				if(mp[ve[i]+ve[j]+ve[k]]==0) {
					cout<<"NO"<<endl;
					return;
				}
			}
		}
	 } 
	
	cout<<"YES"<<endl;
}


signed main()
{
  	int t=1;
  	cin>>t;
  	while(t--) solve();
  	return 0;
}

H. Fight Against Monsters
本质上其实是一个贪心的问题,当时我想的是用前缀和来减少时间复杂度,但是这样还是会超时,所以改变一下记录代价的思维,记录每个怪物攻击的次数,最后一步计算总代价的时候直接加上去大概就是O(n)的复杂度,我们排序的时候要注意结合次数和攻击来排序,并不是攻击力越大的排在越前面,举个例子4 5和3 2,代码有注解

点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
int swj=1;
typedef pair<int,int>pii;
struct a{
	int hp;
	int atk;
	int cnt;
}ve[100005];
 

int num(int x)//计算杀死一个怪物要几次 
{
	int sum=0,cnt=1;
	while(x>0)
	{
		sum++;
		x-=cnt++;
	}
	return sum;
}

bool cmp(a xx,a yy){
	return xx.atk*yy.cnt>yy.atk*xx.cnt;//并不是伤害大就排前面 要结合出现次数 
} 

void solve()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>ve[i].hp>>ve[i].atk;
		int x=ve[i].hp;
		ve[i].cnt=num(x);//num用来计算杀死这个怪物要几次 
	}
	
	sort(ve+1,ve+n+1,cmp);

	
	//与我思路不同在于最小代价的计算人家是算每个怪物的总次数
	//比如样例1攻击力6次 2攻击了4次 3攻击了2次 代价为19 
	int ans=0,cntt=0;
	for(int i=1;i<=n;i++)
	{
		cntt+=ve[i].cnt;
		ans+=cntt*ve[i].atk;
	}
	cout<<"Case #"<<swj++<<": ";
	cout<<ans<<endl;
}


signed main()
{
  	int t=1;
  	cin>>t;
  	while(t--) solve();
  	return 0;
}

B. Nana Likes Polygons
湖北省赛B题,我们要凸多边形的最小值,那么就是三角形,只要通过暴力遍历输入的点找最小值并更新即可,如果找不到最小值就是0,注意输出的时候要开到小数点后6位,不然是过不去的,还有注意更新最小值的条件

点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int,int>pii;
void solve()
{
	int n;
	cin>>n;
	vector<pii>ve(n+1);
	for(int i=1;i<=n;i++)
	{
		cin>>ve[i].first>>ve[i].second;
	}
	
	double   minn=1e15;
	bool check=0;
	for(int i=1;i<=n-2;i++)
	{
		for(int j=i+1;j<=n-1;j++)
		{
			for(int k=j+1;k<=n;k++)
			{
			int x1=ve[i].first,y1=ve[i].second;	
			int x2=ve[j].first,y2=ve[j].second;	
			int x3=ve[k].first,y3=ve[k].second;				
			double area=fabs((x1*y2+x2*y3+x3*y1-x1*y3-x3*y2-x2*y1)/2.0);
				//若无法构成三角形就是0 
			if(area>0) 
			{
				minn=min(minn,area);
				check=1;
			}
			//cout<<area<<endl;
			}
		}
	}
	
	if(!check) cout<<-1<<endl;
	else printf("%.7lf\n",minn);
	
}


signed main()
{
  	int t=1;
  	cin>>t;
  	while(t--) solve();
  	return 0;
}

A. Long Live
x×y=gcd(x,y)*lcm(x,y),那么把这坨代入进去以后就是a ^ 2×b=x×y/gcd(x,y)^2,把a除过去,当a最小时ab最大,因为a只会是整数,所以a为1时这就成立了,而左边的这坨是可以算出来的定值

点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
void solve()
{
	int x,y;
	cin>>x>>y;
	int fm=gcd(x,y);
	cout<<1<<" "<<(x*y)/(fm*fm)<<endl;
	
}


signed main()
{
  	int t=1;
  	cin>>t;
  	while(t--) solve();
  	return 0;
}

E. Equality
这一题本质上也是个贪心的问题,但是遇到的类型比较奇特,所以要学习一下,理解完题意以后我们可以这样做,第一步,先找所有数字里面的最大值和最小值,若最大值等于最小值说明数字全都相等没有答案,若有最大值和最小值,便再看k是否为1,若为1,也不符合题意,符合题意以后,我们大概的思路就是枚举左端点,若左端点为最小值则跳过,下标++,若不是最小值,我们检查这个端点的下一个最小值的位置是否合法,若合法更新下标,不合法则往左退一位再更新下标,因为此时左边已经全为最小值

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long

void solve()
{
	int n,k;
	int maxx=-1,minn=1e9;
	cin>>n>>k;
	vector<int>ve(n+1);
	for(int i=1;i<=n;i++)
	{
		cin>>ve[i];
		maxx=max(ve[i],maxx);
		minn=min(ve[i],minn);
	}
    
    if(maxx==minn) cout<<0<<endl;
	else {
		if(k==1) cout<<"-1"<<endl;
		else {
			vector<int>pos(n+1,1e9);
			//预处理每一数字的最小值的下一个位置
			for(int i=n,p=1e9;i>=1;i--){
				pos[i]=p;
				if(ve[i]==minn) p=i;
			} 
			
			int sum=0;
			for(int i=1;i<=n;i++)
			{
				if(ve[i]==minn) continue;
				sum++;
				//检查不为最小值的点的下一个最小值的位置是否合法,如果可以直接换下标
				//如果不可以,则左端点往左移一格
				if(pos[i]>=i+k) i--;
				i+=k-1;// + k-1是因为更新完后i会自己++ 如果是+k就会跳过一个点
			}
			
			cout<<sum<<endl;
			
		}
		
		
	}
    
    
    
}

signed main(){
	int t=1;
	cin>>t;
	while(t--) solve();
	return 0;
}

J. Points on the Number Axis A
这题还没理解,还得问问再补

L. LCMs
这题也还没理解,还得补

posted on 2024-05-04 23:55  swj2529411658  阅读(48)  评论(3编辑  收藏  举报

导航