【学习笔记】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\n",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\n",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\n",i,it1,it2,val);// printf("%lld\n",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\n",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
posted @ 2019-11-26 21:00  寒雨微凝  阅读(2143)  评论(0编辑  收藏  举报