• Angle Beats Gym


    Angle Beats

    [Time Limit: 4000 ms quad Memory Limit: 1048576 kB ]

    题意

    给出 (n) 个初始点以及 (q) 次询问,每次询问给出一个询问点 (Q),求包括 (Q) 点的直角三角形有多少个。保证 (n+q) 个点都不重复。

    思路

    1. 对于每次询问,当 (Q) 为直角点时,以 (Q) 为原点,对 (n) 个点做象限极角排序,然后用双指针 (L)(R) 维护直角三角形的个数。 (L) 指针用来枚举其中的一条直角边, (R) 指针用来寻找在另一条直角边上的点有多少个,每次找 (QL) 这条边逆时针方向的另一条边(QR)。所以当 (L) 往逆时针转动时,(R) 也会往逆时针转动,那么就可以用双指针直接维护出来了,特别注意一下多个点在同一条直线上的情况就可以了。
    2. (Q) 不是直角点时,可以离线处理,把 (n+q) 个点全部存出来,然后枚举以 (n) 个初始点为直角点时,对哪些的 (Q) 点有贡献,维护方法同上。

    最后的复杂度为 (Oleft(qnC_1 + n(n+q)C_2 ight))(C_1、C_2) 取决于在枚举直角点为原点后,到原点在同一条直线上的点数量。
    我试过把 (n+q) 个节点全部提取出来,然后暴力枚举每个点为直角点的情况,但是这样复杂度会 (T)

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const int maxn = 1e4+10;
    
    struct Point {
    	ll x, y;
    	int id;
    } p[maxn], be[maxn];
    int n, m;
    int ans[maxn];
    
    int cmp1(Point a, Point b) {
    	ll d = a.x*b.y - b.x*a.y;
    	if(d == 0) {
    		return a.x<b.x;
    	} else {
    		return d>0;
    	}
    }
    int Qua(Point a) {
    	if(a.x>0 && a.y>=0)	return 1;
    	if(a.x<=0 && a.y>0)	return 2;
    	if(a.x<0 && a.y<=0)	return 3;
    	if(a.x>=0 && a.y<0)	return 4;
    }
    
    int cmp(Point a, Point b) {
    	if(Qua(a) == Qua(b))	return cmp1(a, b);
    	else	return Qua(a)<Qua(b);
    }
    
    ll check(Point a, Point b) {
    	return a.x*b.x + a.y*b.y;
    }
    
    ll chaji(Point a, Point b) {
    	return a.x*b.y - b.x*a.y;
    }
    
    ll work(Point pp) {
    	for(int i=1; i<=n; i++) {
    		p[i] = be[i];
    		p[i].x -= pp.x;
    		p[i].y -= pp.y;
    	}
    	p[0] = pp;
    	sort(p+1, p+1+n, cmp);
    	for(int j=1; j<=n; j++) {
    		p[j+n] = p[j];
    	}
    	ll ans = 0;
    	int R = 2;
    	for(int L=1; L<=n; L++) {
    		while(R<=2*n) {
    			if(chaji(p[L], p[R]) < 0)	break;
    			if(check(p[L], p[R]) <= 0)	break;
    			R++;
    		}
    		int tR = R;
    		while(tR<=2*n) {
    			if(chaji(p[L], p[tR]) <= 0)	break;
    			if(check(p[L], p[tR]) != 0)	break;
    			ans++;
    			tR++;
    		}
    	}
    	return ans;
    }
    
    int main(){
    	// freopen("in", "r", stdin);
    	while(~scanf("%d%d", &n, &m)) {
    		int all = 0;
    		for(int i=1; i<=n; i++) {
    			all++;
    			int x, y;
    			scanf("%d%d", &x, &y);
    			p[all].x = x, p[all].y = y, p[all].id = 0;
    			be[all] = p[all];
    		}
    		for(int i=1; i<=m; i++) {
    			all++;
    			int x, y;
    			scanf("%d%d", &x, &y);
    			p[all].x = x, p[all].y = y, p[all].id = i;
    			be[all] = p[all];
    			ans[i] = work(p[all]);
    		}
    		for(int i=1; i<=n; i++) {
    			for(int j=1; j<=all; j++) {
    				p[j] = be[j];
    			}
    			p[0] = be[i];
    			int flag = 0;
    			for(int j=1; j<=all; j++) {
    				if(p[j].x == p[0].x && p[j].y == p[0].y)	flag = 1;
    				if(flag)	p[j] = p[j+1];
    				p[j].x -= p[0].x;
    				p[j].y -= p[0].y;
    			}
    
    			int nn = all-1;
    			sort(p+1, p+1+nn, cmp);
    			for(int j=1; j<=nn; j++) {
    				p[j+nn] = p[j];
    			}
    			int R = 2;
    			for(int L=1; L<=nn; L++) {
    				int id = 0;
    				if(p[0].id)	id = p[0].id;
    				if(p[L].id)	id = p[L].id;
    				while(R<=2*nn) {
    					if(chaji(p[L], p[R]) < 0)	break;
    					if(check(p[L], p[R]) <= 0)	break;
    					R++;
    				}
    				int tR = R;
    				while(tR<=2*nn) {
    					if(chaji(p[L], p[tR]) <= 0)	break;
    					if(check(p[L], p[tR]) != 0)	break;
    					if(id == 0)	{
    						if(p[tR].id)	ans[p[tR].id]++;
    					} else {
    						if(p[tR].id == 0)	ans[id]++;
    					}
    					tR++;
    				}
    			}
    		}
    		for(int i=1; i<=m; i++) {
    			printf("%d
    ", ans[i]);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    vue列表渲染之基本列表
    vue之列表排序计算属性的应用
    IDEA部署远程部署docker
    springboot多数据源《二》
    docker容器之间的通信方式
    batocera添加游戏
    DarwinStreamingServer
    ffmepg,视频流,Darwin Streaming Server, EasyDarwin
    @Cacheable unless的写法,绕不开的SpEL
    绘文字emoji
  • 原文地址:https://www.cnblogs.com/Jiaaaaaaaqi/p/11631203.html
Copyright © 2020-2023  润新知