JSOI2018(战争-Minkowski sum)

本文介绍了一种高效算法来判断两个凸包在平移后是否有交点。通过计算凸包的Minkowski和简化问题,实现快速查询。适用于处理大量点集和平移查询的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

给你2个点集,分别对这2个点集求凸包A,B,每次询问问凸包B平移向量(dx.dy)(dx.dy)后是否与凸包A有公共点。点集大小n,m,询问次数q的范围为1e5

b⃗ +(dx,dy)=a⃗ b→+(dx,dy)=a→
(dx,dy)=a⃗ b⃗ (dx,dy)=a→−b→
我们定义向量集合A,B的Minkowski sum为A+B={a+b|aA,bB}A+B={a+b|a∈A,b∈B}
因此合法的(dx,dy)(dx,dy)范围为:A和-B(B集合所有向量取反)的Minkowski sum
在维基百科上有Minkowski addition的线性做法

For two convex polygons P and Q in the plane with m and n vertices, their Minkowski sum is a convex polygon with at most m + n vertices and may be computed in time O (m + n) by a very simple procedure, which may be informally described as follows. Assume that the edges of a polygon are given and the direction, say, counterclockwise, along the polygon boundary. Then it is easily seen that these edges of the convex polygon are ordered by polar angle. Let us merge the ordered sequences of the directed edges from P and Q into a single ordered sequence S. Imagine that these edges are solid arrows which can be moved freely while keeping them parallel to their original direction. Assemble these arrows in the order of the sequence S by attaching the tail of the next arrow to the head of the previous arrow. It turns out that the resulting polygonal chain will in fact be a convex polygon which is the Minkowski sum of P and Q.

#include<bits/stdc++.h>
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define Rep(i,n) for(int i=0;i<n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define ForkD(i,k,n) for(int i=n;i>=k;i--)
#define RepD(i,n) for(int i=n;i>=0;i--)
#define Forp(x) for(int p=Pre[x];p;p=Next[p])
#define Forpiter(x) for(int &p=iter[x];p;p=Next[p])  
#define Lson (o<<1)
#define Rson ((o<<1)+1)
#define MEM(a) memset(a,0,sizeof(a));
#define MEMI(a) memset(a,127,sizeof(a));
#define MEMi(a) memset(a,128,sizeof(a));
#define INF (2139062143)
#define F (100000007)
#define pb push_back
#define mp make_pair 
#define fi first
#define se second
#define vi vector<int> 
#define pi pair<int,int>
#define SI(a) ((a).size())
#define gmax(a,b) a=max(a,b)
#define ALL(x) (x).begin(),(x).end()
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
ll mul(ll a,ll b){return (a*b)%F;}
ll add(ll a,ll b){return (a+b)%F;}
ll sub(ll a,ll b){return (a-b+llabs(a-b)/F*F+F)%F;}
void upd(ll &a,ll b){a=(a%F+b%F)%F;}
int read()
{
    int x=0,f=1; char ch=getchar();
    while(!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();}
    while(isdigit(ch)) { x=x*10+ch-'0'; ch=getchar();}
    return x*f;
} 
class P{
public:
    ll x,y;
    P(ll x=0,ll y=0):x(x),y(y){}

    friend P operator- (P A,P B) { return P(A.x-B.x,A.y-B.y); }
    friend P operator+ (P A,P B) { return P(A.x+B.x,A.y+B.y); }
    friend P operator* (P A,ll p) { return P(A.x*p,A.y*p); }
    friend P operator/ (P A,ll p) { return P(A.x/p,A.y/p); }
    friend bool operator< (const P& a,const P& b) {return a.x-b.x<0 ||(a.x==b.x&& a.y<b.y );}
}; 
P read_point() {
    P a;
    a.x=read(),a.y=read();
    return a;   
} 
bool operator==(const P& a,const P& b) {
    return a.x==b.x&&a.y==b.y;
} 
typedef P V;
ll cross(V a,V b) {
    return a.x*b.y-a.y*b.x;
}

void print(double a) {
    printf("%.6lf",a); 
}
void print(P p) {
    printf("(%.6lf,%.6lf)",p.x,p.y);
}
template<class T>
void print(vector<T> v) {
    sort(v.begin(),v.end());
    putchar('[');
    int n=v.size();
    Rep(i,n) {
        print(v[i]);
        if (i<n-1) putchar(','); 
    }
    puts("]");
}

typedef vector<P> Polygon ;
bool isPointInConvex(P p,Polygon poly){ // Counterclockwise  inside 1 outside 0
    int n=SI(poly);
    if(n<3) return 0;
    if(cross(p-poly[0],poly[1]-poly[0])>=0) return 0;
    if(cross(p-poly[0],poly[n-1]-poly[0])<=0) return 0;

    int i=2,j=n-1;
    int line=-1;
    while(i<=j) {
        int mid=(i+j)>>1;
        if(cross(p-poly[0],poly[mid]-poly[0])>=0) {
            line=mid;
            j=mid-1;
        }
        else i=mid+1;
    }
    return cross(p-poly[line-1],poly[line]-poly[line-1])<0;
}

bool isPointInConvex2(P p,Polygon &poly){ // Counterclockwise  inside or edge 1 outside 0
    int n=SI(poly);
    if(cross(p-poly[0],poly[1]-poly[0])>0) return 0;
    if(cross(p-poly[0],poly[n-1]-poly[0])<0) return 0;
    int i=2,j=n-1;
    int line=-1;
    while(i<=j) {
        int mid=(i+j)>>1;
        if(cross(p-poly[0],poly[mid]-poly[0])>=0) {
            line=mid;
            j=mid-1;
        }
        else i=mid+1;
    }
    return cross(p-poly[line-1],poly[line]-poly[line-1])<=0;
}
int ConvexHull(P *p,int n,P *ch) {
    sort(p,p+n);
    int m=0;
    Rep(i,n) {
        while(m>1 && cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0) m--;
        ch[m++]=p[i];
    }
    int k=m;
    RepD(i,n-2) {
        while(m>k && cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0) m--;
        ch[m++]=p[i];
    }
    if ( n > 1 ) m--;
    return m;
}
void simplify(Polygon& poly) {
    Polygon ans;
    int n=SI(poly);
    Rep(i,n) {
        if (cross(poly[i]-poly[(i+1)%n],poly[(i+1)%n]-poly[(i+2)%n])!=0)
            ans.pb(poly[(i+1)%n]);
    }
    n=SI(ans);
}

int n,m,q;
Polygon a,b;
bool AngleCmp(V a,V b){
    return atan2(a.y,a.x)<atan2(b.y,b.x);
}
Polygon Minkowski_addition(Polygon a,Polygon b) {//clockwise
    vector<V> v;
    int n=SI(a),m=SI(b);
    Rep(i,n) {
        v.pb(a[(i+1)%n]-a[i]);
    }
    Rep(i,m) {
        v.pb(b[(i+1)%m]-b[i]);
    }
    sort(ALL(v),AngleCmp);
    v.resize(n+m);

    ll mya=a[0].y;
    Rep(i,n) gmax(mya,a[i].y);
    ll myb=b[0].y;
    Rep(i,m) gmax(myb,b[i].y);
    ll mxa=a[0].x;
    Rep(i,n) gmax(mxa,a[i].x);
    ll mxb=b[0].x;
    Rep(i,m) gmax(mxb,b[i].x);

    For(i,n+m-1) {
        v[i]=v[i]+v[i-1];
    }
    ll vx=v[0].x,vy=v[0].y;
    For(i,n+m-1) {
        gmax(vx,v[i].x);
        gmax(vy,v[i].y);
    }
    P vv=P(mxa+mxb-vx,mya+myb-vy);
    Rep(i,n+m) v[i]=v[i]+vv;    
    return v;
}
#define MAXN (1033456)
P p[MAXN],ch[MAXN];
int main()
{
//  freopen("war10.in","r",stdin);
//  freopen("war.out","w",stdout);
    cin>>n>>m>>q;
//  int st=clock();
    Rep(i,n) p[i]=read_point();
    n=ConvexHull(p,n,ch);   
    Rep(i,n) a.pb(ch[i]);

    Rep(i,m) p[i]=read_point();
    Rep(i,m) p[i].x*=-1,p[i].y*=-1;
    m=ConvexHull(p,m,ch);   
    Rep(i,m) b.pb(ch[i]);
    Polygon c= Minkowski_addition(a,b);
    simplify(c);
    For(i,q) {
        P q;q.x=read(),q.y=read();
        int d=isPointInConvex2(q,c);
        printf("%d\n",d);
    }
//  cerr<<(double)(clock()-st)/CLOCKS_PER_SEC<<endl;
    return 0;
}
### JSOI 星球大战 相关题目及解法 #### 题目背景 很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治着整个星系。反抗军正在计划一次大规模的反攻行动[^2]。 #### 题目描述 给定一张图表示星系中的行星及其连接关系,每颗行星可以看作是一个节点,而边则代表两颗行星之间的通信通道。初始时所有行星都是连通的。然而,随着时间推移,某些行星可能被摧毁,从而影响到整体网络的连通性。每次询问需要返回当前还剩下多少个连通分量。 该问题的核心在于动态维护图的连通性变化情况,并快速响应查询操作。 --- #### 解决方案概述 此问题可以通过 **并查集 (Disjoint Set Union, DSU)** 数据结构来高效解决。以下是具体实现方法: 1. 并查集是一种用于处理不相交集合的数据结构,支持两种主要操作: - `find(x)`:找到元素 $x$ 所属集合的根节点。 - `union(x, y)`:将两个不同集合合并成一个新的集合。 这些操作的时间复杂度接近常数级别(通过路径压缩优化后为 $\alpha(n)$),其中 $\alpha(n)$ 是阿克曼函数的逆函数。 2. 对于本题而言,由于是倒序模拟行星毁灭的过程,因此可以从最终状态向前回溯重建历史记录。即先假设所有的行星都被摧毁了,再逐步恢复它们的存在状态。 3. 使用数组存储每个时间点上的事件顺序,按照输入数据给出的销毁次序依次执行相应的动作即可完成任务需求。 --- #### 实现细节 下面提供了一个基于 Python 的解决方案框架: ```python class UnionFind: def __init__(self, n): self.parent = list(range(n)) self.rank = [0] * n def find(self, x): if self.parent[x] != x: self.parent[x] = self.find(self.parent[x]) # 路径压缩 return self.parent[x] def union_set(self, x, y): xr = self.find(x) yr = self.find(y) if xr == yr: return False if self.rank[xr] < self.rank[yr]: self.parent[xr] = yr elif self.rank[xr] > self.rank[yr]: self.parent[yr] = xr else: self.parent[yr] = xr self.rank[xr] += 1 return True def main(): import sys input = sys.stdin.read data = input().split() N, M = int(data[0]), int(data[1]) edges = [] for i in range(M): u, v = map(int, data[i*2+2:i*2+4]) edges.append((u-1, v-1)) # Convert to zero-based index destroyed_order = list(map(lambda x:int(x)-1, data[M*2+2:M*2+N+2])) queries = [] uf = UnionFind(N) current_components = N result = [] # Preprocess the reverse order of destructions. active_edges = set(edges) edge_map = {tuple(sorted(edge)): idx for idx, edge in enumerate(edges)} status = [True]*M for planet in reversed(destroyed_order): initial_state = current_components connected_to_planet = [ e for e in active_edges if planet in e and all(status[edge_map[tuple(sorted(e))]] for e in active_edges)] for a, b in connected_to_planet: if uf.union_set(a, b): current_components -= 1 result.append(current_components) queries.insert(0, str(initial_state)) print("\n".join(reversed(result))) if __name__ == "__main__": main() ``` 上述代码定义了一个简单的并查集类以及主程序逻辑部分。它读取标准输入流中的数据,构建所需的邻接表形式表达图的关系矩阵;接着依据指定好的破坏序列逐一还原各阶段下的实际状况直至结束为止。 --- #### 性能分析 对于最大规模测试案例来说 ($N=10^5$, $M=4 \times 10^5$),这种方法能够很好地满足性能要求。因为每一次联合操作几乎都可以视为 O(α(N)) 时间消耗,所以总体运行效率非常高。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值