Codeforces Round #492 (Div. 2)E. Leaving the Bar

链接

http://codeforces.com/contest/996/problem/E

题目大意

给你 n 个向量,长度都小于 10 6 ,让你确定每个向量的符号,使得他们最后加起来长度小于 1.5 × 10 6

算法雏形

网上都是什么随机化+贪心,太玄学了,虽然也不错,但我还是喜欢非玄学算法。
三个向量分成一组,每个向量取正负就能变成两个向量;六个向量中选出两个向量,最小夹角满足 θ [ 0 , 60 ] ,假设拥有最小夹角的两个向量为 x , y ,那么 | x | , | y | 10 6

| x y | = | x | 2 + | y | 2 2 | x | | y | c o s θ

c o s θ [ 1 2 , 1 ]

2 | x | | y | c o s θ [ 2 | x | | y | , | x | | y | ]

| x y | [ | x | 2 + | y | 2 2 | x | | y | , | x | 2 + | y | 2 | x | | y |   ]

把这个东西看作以 b 为自变量的二次函数,求个导或者直接用判别式加韦达定理,可以证明这个东西不大于 10 6 (除以 | x | | y | 再乘以 | x | | y | )
那就很显然啦,不停的选择前三个向量进行合并,最后剩下两个的时候,两个向量的长度肯定都不小于 10 6 ,同样每个向量拆成正负两个,得到夹角 α [ 0 , 90 ] ,再用同样的方法推导出,最后向量的模的最大值为 2 × 10 6 < 1.5 × 10 6
现在最棘手的问题就在于,如何记录向量的正负?

并查集思想

并查集是树这一点很显然
每次合并两个对象,我就让其中一个对象成为另一个对象的父亲,同时在父亲节点记录最新的向量。
每次给向量乘以 1 或者 1 ,实则是给这个集合中每个元素乘以 1 1 ,我只需要在父亲节点打一个标记,这个标记不需要下放,只需要在最后我要计算答案的时候,对于每个节点都沿着父亲一路扫上去,所有标记的乘积就是这个点最终个的符号。(记忆化以做到 O ( n ) )

时间复杂度

O ( n )

补充

启发式合并也可以,时间复杂度 O ( n log n )

代码

#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#define maxn 100010
#define lim (1e6+1e-8)
using namespace std;
struct vec
{
    int num;
    double x, y;
    double mod(){return sqrt(x*x+y*y);}
    vec operator+(vec v){return (vec){0,x+v.x,y+v.y};}
    vec operator*(int t){return (vec){0,t*x,t*y};}
}v[maxn];
int n, f[maxn], c[maxn], C, tag[maxn];
queue<vec> q;
inline void link(int ca, vec &a, int cb, vec &b)
{
    f[b.num]=a.num;
    tag[a.num]*=ca;
    tag[b.num]*=cb*tag[a.num];
    q.push((vec){a.num,ca*a.x+cb*b.x,ca*a.y+cb*b.y});
}
void init()
{
    int i, j;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
        v[i].num=i;
        tag[i]=1;
        f[i]=i;
        scanf("%lf%lf",&v[i].x,&v[i].y);
        q.push(v[i]);
    }
}
void work()
{
    vec v1, v2, v3;
    int c1, c2, c3, flag;
    while(q.size()>2)
    {
        v1=q.front(), q.pop();
        v2=q.front(), q.pop();
        v3=q.front(), q.pop();
        flag=1;
        for(c1=-1;c1<=1 and flag;c1+=2)
            for(c2=-1;c2<=1 and flag;c2+=2)
                for(c3=-1;c3<=1 and flag;c3+=2)
                {
                    if((v1*c1+v2*c2).mod()<lim)link(c1,v1,c2,v2), q.push(v3), flag=0;
                    else if((v1*c1+v3*c3).mod()<lim)link(c1,v1,c3,v3), q.push(v2), flag=0;
                    else if((v2*c2+v3*c3).mod()<lim)link(c2,v2,c3,v3), q.push(v1), flag=0;
                }
    }
    v1=q.front(), q.pop();
    v2=q.front(), q.pop();
    if((v1+v2).mod()<(v1+v2*(-1)).mod())link(1,v1,1,v2);
    else link(1,v1,-1,v2);
}
int calc(int x)
{
    if(c[x])return c[x];
    if(x==f[x])c[x]=tag[x];
    else c[x]=tag[x]*calc(f[x]);
    return c[x];
}
int main()
{
    init();
    if(n==1)
    {
        printf("1");
        return 0;
    }
    work();
    for(int i=1;i<=n;i++)if(!c[i])calc(i);
    for(int i=1;i<=n;i++)printf("%d ",c[i]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/fsahfgsadhsakndas/article/details/80976631