luogu P9571 「NnOI R2-T3」Horizon Blue 题解

原题链接

题意

小 C 喜欢在画板上画画。他进行了 $ n $ 次操作,每次操作有如下三种可能:

  • 1 k b 代表小 C 绘制了一条解析式为 $ y=kx+b $ 的直线。
  • 2 k b 代表小 C 询问你直线 $ y=kx+b $ 与多少条被绘制的直线有恰好一个公共点。
  • 3 k b 代表小 C 擦除所有与直线 $ y=kx+b $ 有至少一个公共点的直线。

思路

本题一开始看起来比较凶,其实并没有那么难,只要熟练掌握 STL 就可以秒。

已知一条直线,与另一条直线有恰好一个公共点,当且仅当两个直线斜率相等,截距不同。至少有一个公共点,当且仅当斜率相等时,结局不相等。

于是我们可以开一个 multiset,利用可重复性来计数。定义下标为 \(k_i\),存储 \(b_i\),利用 counteraseclear 等函数等维护同斜率下截距的不同情况,最终得出答案。

注意在维护的过程中记录斜率的上下限,防止大规模遍历的超时。

本程序时间复杂度判断较复杂,上下限较难判断,故不展开讨论。

具体方法详见代码:

代码

#include<bits/stdc++.h>
#define RG register
#define IL inline
using namespace std;
const int maxn = 200005;
IL int Read(); IL void Rite(int x);
multiset<int> L[maxn];
int n,cnt = 0;
int mink = INT_MAX,maxk = 0,p;
int main(){
  n = Read();
  for(int i = 1;i <= n;++i){
    int opt = Read(), k = Read() + 100000, b = Read();
    if(opt == 1){//绘制
	  mink = min(k,mink);
	  maxk = max(k,maxk);
      L[k].insert(b);//插入
      cnt++;
    }	  
	else if(opt == 2){//询问
	  Rite(cnt - L[k].size());
	  putchar('\n');
	}
	else{//删除
      for(RG int it = mink;it <= maxk;++it)
	  	if(it != k) L[it].clear();//清空
      p = L[k].count(b);//计数
      if(p > 0) L[k].erase(b);//擦除
 	  cnt = L[k].size();//大小
	  mink = k;
	  maxk = k; 	 //注意 
	}

  }
  return 0;
}
IL int Read(){
  char c = getchar();
  int x = 0,f = 1;
  while(c < '0' || c > '9'){
    if(c == '-') f = -1;
    c = getchar();
  }
  while(c >= '0'&&c <= '9'){
    x = x * 10 + c - '0';
    c = getchar();
  }
  return x * f;
}
IL void Rite(int x){
  if(x < 0) putchar('-'),x = -x;
  if(x > 9) Rite(x / 10);
  putchar(x % 10 + '0');
}

posted @ 2023-08-23 09:53  CultReborn  阅读(11)  评论(0)    收藏  举报