zoukankan      html  css  js  c++  java
  • 【学习笔记】Minkowski和

    这还是个被我咕了N久的玩意

    Minkowski和是一个奇怪的玩意

    他长这样

    $S={a+b | a in A , b in B}$

    AB可以是点集也可是向量集(显然)

    他可以处理一些奇怪的东西

    比如说我们来看这个题

    JSOI2018 战争

    你发现它要求的就是判断向量是否存在于A-B的Minkowski和里

    那么你套上板子就做完了

    好了你大概了解了Minkowski和是什么

    我们现在来学怎么写

    我们根据直观理解 Minkowski上的点一定是点集构成的凸包上的点

    于是我们暴力求出所有点再进行一次求凸包就做完了 复杂度是 O(|A|*|B|)

    它看起来就不是很优 肯定可以优化

    我们发现很好的性质 凸包上的点它的斜率是单调的 所以显然可以TwoPointers优化

    我们直接观察哪一个在外面拓展就可以了

    代码实现扔这里了

    //Love and Freedom.
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #define inf 20021225
    #define ll long long
    #define db double
    #define eps 1e-8
    #define N 200010
    using namespace std;
    int read()
    {
        int f=1,s=0; char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
        while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
        return f*s;
    }
    struct poi
    {
        db x,y;
        poi(){}
        poi(db _x,db _y){x=_x,y=_y;}
    };
    typedef poi vec;
    vec operator+(vec a,vec b){return vec(a.x+b.x,a.y+b.y);}
    vec operator-(vec a,vec b){return vec(a.x-b.x,a.y-b.y);}
    vec operator*(vec a,db b){return vec(a.x*b,a.y*b);}
    vec operator/(vec a,db b){return vec(a.x/b,a.y/b);}
    db cross(vec a,vec b){return a.x*b.y-a.y*b.x;}
    db dot(vec a){return a.x*a.x+a.y*a.y;}
    db len(vec a){return sqrt(dot(a));}
    db dis(poi a,poi b){return len(b-a);}
    poi p0;
    int dcmp(db x){return x>eps?1:x<-eps?-1:0;}
    bool cmp(poi p1,poi p2){return dcmp(cross(p1-p0,p2-p0))==1||(dcmp(cross(p1-p0,p2-p0))==0&&dis(p0,p1)<dis(p0,p2));}
    int gethull(poi *p,poi *h,int n)
    {
        p0=poi{1e18,1e18}; int id=0;
        for(int i=1;i<=n;i++)
            if(dcmp(p[i].x-p0.x)<0||(dcmp(p[i].x-p0.x)==0&&dcmp(p[i].y-p0.y)<0))
                id=i,p0=p[i];
        swap(p[1],p[id]); sort(p+2,p+n+1,cmp);
        int top=2; h[1]=p[1],h[2]=p[2];
        for(int i=3;i<=n;i++)
        {
            while(top>2&&dcmp(cross(p[i]-h[top-1],h[top]-h[top-1]))>=0)    top--;
            h[++top]=p[i];
        }
        return top;
    }
    poi A[N],B[N],p[N],C[N]; int na,nb,nc;
    void minkowski()
    {
        A[na+1]=A[1],B[nb+1]=B[1];
        C[nc=1]=A[1]+B[1]; int i=1,j=1;
        while(i<=na&&j<=nb)
        {
            vec v1=A[i+1]+B[j]-C[nc],v2=A[i]+B[j+1]-C[nc];
            if(dcmp(cross(v1,v2))>=0)
                C[++nc]=A[i+1]+B[j],i++;
            else
                C[++nc]=A[i]+B[j+1],j++;
        }
        while(i<=na)    C[++nc]=A[i]+B[j],i++;
        while(j<=nb)    C[++nc]=A[i]+B[j],j++;
    }
    bool check(poi w)
    {
        if((dcmp(cross(w-C[1],C[nc]-C[1]))==0&&dis(C[nc],C[1])>=dis(C[1],w))||(dcmp(cross(w-C[1],C[2]-C[1]))==0 && dis(C[1],w)<=dis(C[2],C[1])))    return 1;
        int l=2,r=nc,ans=0;
        while(l<=r)
        {
            int mid=l+r>>1; poi p1=C[mid];
            if(dcmp(cross(p1-C[1],w-C[1]))>=0)    l=mid+1,ans=mid;
            else    r=mid-1;
        }
        if(ans==nc||!ans)    return 0;
        poi p1=C[ans],p2=C[ans+1];
        if(dcmp(cross(p1-w,p2-w))>=0)    return 1;
        return 0;
    }
    int main()
    {
        int n1=read(),n2=read(),q=read();
        for(int i=1;i<=n1;i++)    p[i].x=read(),p[i].y=read();
        na=gethull(p,A,n1);
        for(int i=1;i<=n2;i++)    p[i].x=-read(),p[i].y=-read();
        nb=gethull(p,B,n2);
        minkowski();
        while(q--)
        {
            poi w; w.x=read(),w.y=read();
            printf("%d
    ",check(w));
        }
        return 0;
    }
    View Code

    注:1.求点是否在凸包内可以直接三角剖分以后二分在哪个极角区间内即可 2.至于为什么我想锤爆我自己的狗头呢 因为我的cmp写了dcmp>1调了一个世纪xtbl

    另一道题

    N校联考的题

    (题面好像没法放/px)

    就是我们首先猜结论 对于奇数和偶数的答案分别是凸的 那么考虑维护奇偶正负共四个凸的答案

    我们分治去做 考虑如何合并

    由于是凸的所以差分单调 那么我们就可以TwoPointers优化

    然后求完答案继续维护差分数组

    (这个玩意貌似还叫做分治max卷积/px)

    发现这个过程其实也是在求Minkowski和

    我的代码是展开讨论写的 于是它贼快但是贼长(

    //Love and Freedom.
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #define inf (ll)(1e18)
    #define ll long long
    #define N 500010
    using namespace std;
    int read()
    {
        int f=1,s=0; char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
        while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
        return f*s;
    }
    ll f[N][2],d[N][2],tmp[N][2],a[N];
    void solve(int l,int r)
    {
        if(l==r){f[l][0]=a[l],f[l][1]=a[l]; d[l][0]=a[l],d[l][1]=a[l]; return;}
        int mid=l+r>>1; solve(l,mid); solve(mid+1,r);
        int it1,it2; ll val;
        for(int i=0;i<=r-l+1;i++)    tmp[i][0]=-inf,tmp[i][1]=inf;
        it1=l+1,it2=mid+1; val=d[l][0];
        for(int i=1;i<=r-l+1;i+=2) // odd l odd+ r even-
        {
            tmp[i][0]=max(tmp[i][0],val);
            if(it2>r-1 && it1>mid-1)    break;
            if(it2>r-1 || (it1<=mid-1&&d[it1][0]+d[it1+1][0]>-d[it2][1]-d[it2+1][1]))
                val+=d[it1][0]+d[it1+1][0],it1+=2;
            else
                val-=d[it2][1]+d[it2+1][1],it2+=2;
        }
        it1=l,it2=mid+2,val=d[mid+1][0];// printf("%lld
    ",val);
        for(int i=1;i<=r-l+1;i+=2) // odd l even+ r odd+
        {
            tmp[i][0]=max(tmp[i][0],val);
            if(it2>r-1 && it1>mid-1)    break;
            if(it2>r-1 || (it1<=mid-1&&d[it1][0]+d[it1+1][0]>d[it2][0]+d[it2+1][0]))
                val+=d[it1][0]+d[it1+1][0],it1+=2;
            else
                val+=d[it2][0]+d[it2+1][0],it2+=2;
        }
        it1=l+1,it2=mid+2,val=d[l][0]-d[mid+1][1];
        for(int i=2;i<=r-l+1;i+=2) // even l odd+ r odd-
        {
            tmp[i][0]=max(tmp[i][0],val);//printf("%d %d %d %lld
    ",i,it1,it2,val);// printf("%lld
    ",val);
            if(it2>r-1 && it1>mid-1)    break;
            if(it2>r-1 || (it1<=mid-1&&d[it1][0]+d[it1+1][0]>-d[it2][1]-d[it2+1][1]))
                val+=d[it1][0]+d[it1+1][0],it1+=2;
            else
                val-=d[it2][1]+d[it2+1][1],it2+=2;
        }
        it1=l,it2=mid+1,val=0;
        for(int i=0;i<=r-l+1;i+=2) // even l even+ r even+
        {
            tmp[i][0]=max(tmp[i][0],val);
            if(it2>r-1 && it1>mid-1)    break;
            if(it2>r-1 || (it1<=mid-1&&d[it1][0]+d[it1+1][0]>d[it2][0]+d[it2+1][0]))
                val+=d[it1][0]+d[it1+1][0],it1+=2;
            else
                val+=d[it2][0]+d[it2+1][0],it2+=2;
        }
        
        it1=l+1,it2=mid+1; val=d[l][1];
        for(int i=1;i<=r-l+1;i+=2) // odd l odd+ r even-
        {
            tmp[i][1]=min(tmp[i][1],val); 
            if(it2>r-1 && it1>mid-1)    break;
            if(it2>r-1 || (it1<=mid-1&&d[it1][1]+d[it1+1][1]<-d[it2][0]-d[it2+1][0]))
                val+=d[it1][1]+d[it1+1][1],it1+=2;
            else
                val-=d[it2][0]+d[it2+1][0],it2+=2;
        }
        it1=l,it2=mid+2,val=d[mid+1][1];
        for(int i=1;i<=r-l+1;i+=2) // odd l even+ r odd+
        {
            tmp[i][1]=min(tmp[i][1],val);
            if(it2>r-1 && it1>mid-1)    break;
            if(it2>r-1 || (it1<=mid-1&&d[it1][1]+d[it1+1][1]<d[it2][1]+d[it2+1][1]))
                val+=d[it1][1]+d[it1+1][1],it1+=2;
            else
                val+=d[it2][1]+d[it2+1][1],it2+=2;
        }
        it1=l+1,it2=mid+2,val=d[l][1]-d[mid+1][0];
        for(int i=2;i<=r-l+1;i+=2) // even l odd+ r odd-
        {
            tmp[i][1]=min(tmp[i][1],val);
            if(it2>r-1 && it1>mid-1)    break;
            if(it2>r-1 || (it1<=mid-1&&d[it1][1]+d[it1+1][1]<-d[it2][0]-d[it2+1][0]))
                val+=d[it1][1]+d[it1+1][1],it1+=2;
            else
                val-=d[it2][0]+d[it2+1][0],it2+=2;
        }
        it1=l,it2=mid+1,val=0;
        for(int i=0;i<=r-l+1;i+=2) // even l even+ r even+
        {
            tmp[i][1]=min(tmp[i][1],val);
            if(it2>r-1 && it1>mid-1)    break;
            if(it2>r-1 || (it1<=mid-1&&d[it1][1]+d[it1+1][1]<d[it2][1]+d[it2+1][1]))
                val+=d[it1][1]+d[it1+1][1],it1+=2;
            else
                val+=d[it2][1]+d[it2+1][1],it2+=2;
        }
        //printf("%d %d
    ",l,r);
        for(int i=1;i<=r-l+1;i++)
            f[l+i-1][0]=tmp[i][0],
            f[l+i-1][1]=tmp[i][1];
        //puts("");
        d[l][0]=f[l][0],d[l][1]=f[l][1];
        for(int i=l+1;i<=r;i++)
            d[i][0]=f[i][0]-f[i-1][0],
            d[i][1]=f[i][1]-f[i-1][1];
    }
    int main()
    {
        int n=read();
        for(int i=1;i<=n;i++)    a[i]=read();
        solve(1,n);
        for(int i=1;i<=n;i++)
            printf("%lld ",f[i][0]);
        return 0;
    }
    View Code
  • 相关阅读:
    php设计模式-工厂模式(一)
    php Pthread 线程 互斥锁
    php Pthread 多线程 Worker
    PHP多进程实例
    C# this 关键字
    .NET FileStream文件流,StreamReader文本流,MemoryStream内存流几种流的实例
    深入理解Static关键字修饰符
    SQL Server 视图
    .NET初识委托
    RSA加密
  • 原文地址:https://www.cnblogs.com/hanyuweining/p/11938404.html
Copyright © 2011-2022 走看看