Codeforces Round #492 (Div. 1) :995C - Leaving the Bar

题目传送门
题意:有一堆向量,你可以将一些取反,使后所有向量之和的长度小于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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值