题目传送门
题意:有一堆向量,你可以将一些取反,使后所有向量之和的长度小于1.5×10^6,保证有解。
思路:最后的总和向量,x ^ 2 + y ^ 2,肯定是小于2.25*10^ 12,而每条边的向量模最大是1 ^6,所以极限情况是2条边都是1 ^6,并且成90°,这时模长最大是√2 * 10 ^6,所以任意两条边的向量和长度小于1.414 * 10 ^6<1.5 * 10 ^6。使用贪心,每次 保证向量和离原点更近,不断合并边即可。
但是有个隐患,因为每次贪心都是针对当前两条边,而并不是在所有边里找最适合那条,所以有可能出现最后模大于1.5 *10 ^6,
比如,第一条边是(1,0),第二条边是(0,1),那么你合成的时候,最后肯定是(1,1),模长刚好是√2(或者是(-1,-1),(1,-1),(-1,1),但他们模长都一样,没啥区别),而第三条边是(√2/2,-√2/2),模长刚好是1,且与(1,1)垂直,无论他们俩取反,都是垂直的,这时合成的模长就是√3了,大于了1.5,所以每次我们贪心完后,判断模长是否小于等于1.5,假如大于了,就交换第二位,依次交换,因为卡模长的边一般只要提前处理就不会出事,所以找到那个卡模长的边,优先处理他即可,比如上面的例子,假如第一条边是(1,0),第二条边是(√2/2,-√2/2),那么处理后模长小于1,再与(0,1)合成的时候一定小于1.5
所以假如本次贪心结果不成立,就将第二位与最后一位交换,再不成立,再交换第二位与倒数第二位,循环此次操作,直到成立
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll q=1500000;
#define maxn 100050
typedef pair<int,int> P;
P a[maxn];
int num[maxn],n;
int fact[maxn];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1; i<=n; ++i) {
cin>>a[i].first>>a[i].second;
num[i]=i;//因为有可能要交换,先预先保存位置
}
ll ans=q*q;
int qwe=n;
while(1) {
ll x=0,y=0;
for(int i=1; i<=n; ++i) {//判断本次是否需要取反
if((x+a[i].first)*(x+a[i].first)+(y+a[i].second)*(y+a[i].second)>(x-a[i].first)*(x-a[i].first)+(y-a[i].second)*(y-a[i].second))
x-=a[i].first,y-=a[i].second,fact[num[i]]=-1;
else
x+=a[i].first,y+=a[i].second,fact[num[i]]=1;
}
if(x*x+y*y<=ans)//判断是否成立
{//成立了,直接输出
for(int i=1;i<=n;i++)
{
cout<<fact[i]<<" ";
}
cout<<"\n";
return 0;
}
swap(a[2].second,a[qwe].second);
swap(a[2].first,a[qwe].first);
swap(num[2],num[qwe--]);//交换
}
return 0;
}