2 条题解

  • 0
    @ 2026-1-26 9:19:02

    分析

    首先想到的一个优化就是考虑去掉一些没用的点。怎么做呢?

    我们可以把满足:

    • x,yx,y 在所有点中出现次数 ≤2\le 2。
    • (x,y)(x,y) 没有访问过。

    这些条件的点存储起来,构造集合 SS,查询时只需要找这些点。

    证明

    现在我们要证明:若 ∣S∣≥5|S|\ge 5,对于任意一个 t(x,y)t(x,y),SS 中存在 p(xp,yp)p(x_p,y_p),满足 xp≠x,yp≠yx_p≠x,y_p≠y。

    设 A={q∈S∣xq=x}A=\{q\in S|x_q=x\}。

    设 B={q∈S∣yq=y}B=\{q\in S|y_q=y\}。

    根据定义,∣A∣≤2,∣B∣≤2|A|\le 2,|B|\le 2,所以 ∣A∪B∣≤∣A∣+∣B∣≤4<∣S∣|A\cup B|\le|A|+|B|\le 4<|S|。

    所以必定存在。

    对于 ∣S∣≤4|S|\le 4:

    如果有解,必然 xp≠x,yp≠yx_p≠x,y_p≠y,在 SS 中。

    如果无解,必然不存在 xp≠x,yp≠yx_p≠x,y_p≠y,SS 中不存在。

    解释

    解释为什么限制 x,yx,y 的出现为 22 不是 11:

    if(mx[qp.x]<=2&&my[qp.y]<=2&&!vis[qp]){
    	++mx[qp.x];
    	++my[qp.y];
    	vis[qp]=1;
    	p.emplace_back((node){qp,i});
    }
    
    

    这是因为可能存在 11 个点是被摧毁的,且 mxx=1mx_x=1,必须限制为 22,才能找到没有被摧毁的那一个点。

    时间复杂度

    与 tt 共享 xx 或 yy 坐标的点最多有 44 个(xx 相同的最多 22 个,yy 相同的最多 22 个)。

    O(nlog⁡n+q)O(n\log n+q),主要是使用了 map。

    //SPJ:???
    // #include<bits/stdc++.h>
    #include<bits/extc++.h>
    #define gp __gnu_pbds::gp_hash_table
    #define endl '\n'
    #define hh puts("")
    //#define int long long
    #define uap unordered_map
    #define uet unordered_set
    #define sr() srand(time(NULL))
    #define rando(cntmax,istart,rng) for(int i=istart,cnt=0;cnt<=cntmax;cnt++,i=(rand()%rng))
    #define lrfor(l,r) for(int cnt=1,i=l,j=r,forid=i;i<j&&l<=i&&i<=r&&l<=j&&j<=r;i+=cnt%2,j-=cnt%2,forid=(cnt%2?j:i),++cnt)
    #define lrfor2(l,r) for(size_t cnt=1,i=l,j=r,forid=i;i<j&&l<=i&&i<=r&&l<=j&&j<=r;i+=cnt%2,j-=cnt%2,forid=(cnt%2?j:i),++cnt)
    #define IAKIOI ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
    #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,mn,stdin),p1==p2)?EOF:*p1++)
    namespace IO{
    	const int mn=1e6+100;
    	char buf[mn],*p1=buf,*p2=buf;
    	inline int read(){
    		int x=0,f=1;char ch=getchar();
    		while (ch<'0'||ch>'9'){if (ch=='-')f=-1;ch=getchar();}
    		while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();}
    		return x*f;
    	}
    	inline void write(int x){
    		if(x<0){putchar('-');x=-x;}
    		if(x>9)write(x/10);
    		putchar(x%10+'0');
    	}
    }using namespace IO;
    const int N=1e6+10;
    using namespace std;
    struct point{
    	int x,y;
    	friend inline bool operator<(point aa,point bb){return(aa.x!=bb.x?aa.x<bb.x:aa.y<bb.y);}
    //	friend inline bool operator==(point aa,point bb){return aa.x==bb.x&&aa.y==bb.y;}
    };
    struct node{
    	point dot;
    	int id;
    };
    gp<int,int> mx,my;
    map<point,bool> vis;
    vector<node> p;
    inline bool check(point che,point man){
    	return man.x!=che.x&&man.y!=che.y;
    }
    signed main(){
    	int n,q;
    	n=read();
    	q=read();
    	for(int i=1;i<=n;++i){
    		point qp;
    		qp.x=read();
    		qp.y=read();
    		if(mx[qp.x]<=2&&my[qp.y]<=2&&!vis[qp]){
    			++mx[qp.x];
    			++my[qp.y];
    			vis[qp]=1;
    			p.emplace_back((node){qp,i});
    		}
    	}
    	while(q--){
    		point t;
    		t.x=read();
    		t.y=read();
    		bool flag=0;
    		for(int i=1;i<p.size();i++){
    			point q=p[i].dot;
    			if(check(t,q)){
    				write(p[i].id);
    				hh;
    				flag=1;
    				break;
    			}
    		}
    		if(!flag)write(-1),hh;
    	}
    	return 0;
    }
    
    • 0
      @ 2025-12-10 21:51:03

      考虑依次选取以下 77 个「关键营地」(需要指出的是,这 77 个「关键营地」不一定都存在):

      • 00 号「关键营地」为输入的第一个营地。
      • 11 号「关键营地」为输入的第一个与 00 号「关键营地」xx 坐标相同,yy 坐标不同的营地。
      • 22 号「关键营地」为输入的第一个与 00 号「关键营地」xx 坐标不同,yy 坐标相同的营地。
      • 33 号「关键营地」为输入的第一个与 00 号「关键营地」xx 坐标不同,yy 坐标不同的营地。
      • 44 号「关键营地」为输入的第一个与 00 号「关键营地」xx 坐标不同,yy 坐标不同,与 33 号「关键营地」xx 坐标相同,yy 坐标不同的营地。
      • 55 号「关键营地」为输入的第一个与 00 号「关键营地」xx 坐标不同,yy 坐标不同,与 33 号「关键营地」xx 坐标不同,yy 坐标相同的营地。
      • 66 号「关键营地」为输入的第一个与 00 号「关键营地」xx 坐标不同,yy 坐标不同,与 33 号「关键营地」xx 坐标不同,yy 坐标不同的营地。

      可以证明,如果某个询问有解,则这 77 个「关键营地」中必有一个是该询问的合法解,证明此处从略。

      最终的时间复杂度为 O(n+q)O(n + q),空间复杂度为 O(1)O(1)。

      #include <bits/stdc++.h>
      using namespace std;
      inline int read()
      {
      	int x = 0;
      	char c = getchar_unlocked();
      	while (c < '0') c = getchar_unlocked();
      	while (c >= '0')
      	{
      		x = x * 10 + c - '0';
      		c = getchar_unlocked();
      	}
      	return x;
      }
      void write(int x)
      {
      	if (x <= 9)
      	{
      		putchar_unlocked(x + '0');
      		return;
      	}
      	write(x / 10);
      	putchar_unlocked(x % 10 + '0');
      }
      inline void write_solution(int x)
      {
      	write(x);
      	putchar_unlocked('\n');
      }
      inline void write_no_solution()
      {
      	putchar_unlocked('-');
      	putchar_unlocked('1');
      	putchar_unlocked('\n');
      }
      struct Point
      {
      	int x, y, i;
      };
      Point p[7];
      int main()
      {
      	freopen("laser.in", "r", stdin);
      	freopen("laser.out", "w", stdout);
      	int n = read(), q = read();
      	p[0] = (Point) {read(), read(), 1};
      	for (int i = 2; i <= n; i++)
      	{
      		int x = read(), y = read();
      		if (x == p[0].x && y != p[0].y) p[1] = (Point) {x, y, i};
      		if (x != p[0].x && y == p[0].y) p[2] = (Point) {x, y, i};
      		if (x != p[0].x && y != p[0].y)
      		{
      			if (!p[3].i)
      			{
      				p[3] = (Point) {x, y, i};
      				continue;
      			}
      			if (x == p[3].x && y != p[3].y) p[4] = (Point) {x, y, i};
      			if (x != p[3].x && y == p[3].y) p[5] = (Point) {x, y, i};
      			if (x != p[3].x && y != p[3].y) p[6] = (Point) {x, y, i};
      		}
      			
      	}
      	while (q--)
      	{
      		int x = read(), y = read();
      		for (int i = 0; i <= 7; i++)
      		{
      			if (i == 7)
      			{
      				write_no_solution();
      				break;
      			}
      			if (x != p[i].x && y != p[i].y && p[i].i)
      			{
      				write_solution(p[i].i);
      				break;
      			}
      		}
      	}
      	return 0;
      }
      
      • 1

      [Sleeping Cup #8 / CFCOI Collection 1] Super Laser Gun

      信息

      ID
      210
      时间
      1000ms
      内存
      512MiB
      难度
      4
      标签
      递交数
      115
      已通过
      12
      上传者