1 条题解

  • 0
    @ 2025-8-24 21:58:39

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Star_Cried
    Nothing is true.Everything is permitted.

    搬运于2025-08-24 21:58:39,当前版本为作者最后更新于2021-03-06 21:57:19,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    P4293 [WC2010]能量场

    题意

    给你 nn 个粒子,每个粒子有两个权值 mi,cim_i,c_i 每个相邻有序对 (a,b)(a,b) 会产生 mamb(cacb)m_am_b(c_a-c_b) 的贡献。现让你处理两个问题:

    1. 找出一个有序对使贡献最大。
    2. 找出一个序列成环后贡献和最大。

    思路

    我们将贡献转化一下:

    mamb(cacb)=macambmbcbmam_am_b(c_a-c_b)=m_ac_am_b-m_bc_bm_a

    那么这就形成了一个叉积的形式。即将每个点 ii 转化为 x=mici,y=mix=m_ic_i,y=m_i 的向量。

    那么第一问就等价于求叉积最大的两个向量。具体怎么求后面再说。

    那么第二问就是将若干个向量依次首尾相接地叉积和。因为所有点都在第一象限,所以这等价于求一个多边形的面积的两倍。(不会的可以自己根据叉积意义推下)

    那么我们让贡献和最大,相当于求一个构成多边形面积最大的序列——凸包。于是我们求一下凸包就行了。

    至于第一问,我们要快速得到两个点叉积的最大值,发现叉积最大的两个点一定在凸包上。并且发现,顺次遍历所有点并用一个指针记录另一个点的位置,发现叉积的绝对值是单调的。那么我们用类似旋转卡壳的方法扫两遍凸包就行了,即正反各扫一遍(因为边界条件可能错误,但扫两遍一定会统计完全)。

    实现

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cctype>
    #include<cstring>
    #include<cmath>
    using namespace std;
    namespace star
    {
    	const int maxn=5e4+10;
    	int n,m=1,ans,Ans[2],q[maxn];
    	struct vec{
    		double x,y;
    		int id;
    		vec(double x=0,double y=0,int id=0):x(x),y(y),id(id){}
    		vec operator + (const vec &a) const {return vec(x+a.x,y+a.y);}
    		vec operator - (const vec &a) const {return vec(x-a.x,y-a.y);}
    		double operator * (const vec &a) const {return x*a.y-y*a.x;}
    		bool operator < (const vec &a) const {return x<a.x or (x==a.x and y<a.y);}
    	}a[maxn];
    	inline void work(){
    		scanf("%d",&n);
    		for(int i=1;i<=n;i++){
    			double a,b;
    			scanf("%lf%lf",&a,&b);
    			star::a[i]=vec(a*b,a,i);
    		}
    		sort(a+1,a+1+n);
    		q[1]=1;
    		for(int i=2;i<=n;i++){
    			while(m>1 and (a[q[m]]-a[q[m-1]])*(a[i]-a[q[m]])<=0) m--;
    			q[++m]=i;
    		}
    		int tmp=m;
    		for(int i=n-1;i;i--){
    			while(m>tmp and (a[q[m]]-a[q[m-1]])*(a[i]-a[q[m]])<=0) m--;
    			q[++m]=i;
    		}
    		for(int i=1,j=2;i<=m;i++){
    			while(fabs(a[q[i]]*a[q[j]])<fabs(a[q[i]]*a[q[j+1]])) j=j%(m-1)+1;
    			double res=a[q[i]]*a[q[j]];
    			if(ans<fabs(res)){
    				ans=fabs(res);
    				if(res>0)Ans[0]=i,Ans[1]=j;
    				else Ans[0]=j,Ans[1]=i;
    			}
    		}
    		for(int i=m,j=m-1;i;i--){
    			while(fabs(a[q[i]]*a[q[j]])<fabs(a[q[i]]*a[q[j==1?m-1:j-1]])) j=j==1?m-1:j-1;
    			double res=a[q[i]]*a[q[j]];
    			if(ans<fabs(res)){
    				ans=fabs(res);
    				if(res>0)Ans[0]=i,Ans[1]=j;
    				else Ans[0]=j,Ans[1]=i;
    			}
    		}
    		printf("%d %d\n%d\n",a[q[Ans[0]]].id,a[q[Ans[1]]].id,m-1);
    		for(int i=1;i<m;i++) printf("%d ",a[q[i]].id);
    	}
    }
    signed main(){
    	star::work();
    	return 0;
    }
    

    补充

    洛谷的另外一篇题解在代码在数据较小时进行了特判以水过第一个测试点,实际上如果不加特判其根本无法通过此题,原因很可能就是没有进行反方向统计答案。

    • 1

    信息

    ID
    3263
    时间
    4000ms
    内存
    500MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者