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\),利用 count
,erase
,clear
等函数等维护同斜率下截距的不同情况,最终得出答案。
注意在维护的过程中记录斜率的上下限,防止大规模遍历的超时。
本程序时间复杂度判断较复杂,上下限较难判断,故不展开讨论。
具体方法详见代码:
代码
#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');
}