前言
RMJ 炸了,建议在 vjudge 上提交。
SP9512
vjudge
题目大意
给定坐标系上的一些点(可能有重复),每次操作移除一列或一行的所有点,问你每次操作能移除多少点(已经移除的点不再统计)。
思路
首先来看最基础的想法。
将坐标系记录,每次删除就一个一个“抹零”。
但这样的话坐标系的空间复杂度就会达到 O(1018)。数组肯定存不下。
d 很大,看来是没办法往这边想了。
注意到 N 相较于 d 要小很多,所以能不能优化一下呢?
由于每次操作都是抹除一列或一行,所以我们可以这样记录:
于是就有了第二种想法:记录每一列/行有的点,统一删除。
空间复杂度为 O(dN),好了一点。
这样算是解决了一半,因为如果统一删去一列/行,另一个数组还是要逐个删。
来个图理解一下:

如果我要删去第 1 列,xfindy[1] 是可以直接清空,可是 yfindx[i]
,i∈[0,3] 里面还要逐一寻找啊。
这时间复杂度有多 O(N) 啊。
于是第三种做法:map+set。
将原先的 xfindy 改成 map<int,set<int> > xfindy,
yfindx 同理。
那就好办了,set 每次操作的复杂度都是 log 的,省下了例子中在 yfindx[i],i∈[0,3] 里寻找。如果不是例子也可以这样优化。
只要 SPOJ 不出毒瘤数据,就能过了。
诶 set 不能重复计算啊,怎么办?
multiset!
看看代码吧。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| #include<bits/stdc++.h> using namespace std; int n,m; map<int,multiset<int> > xfindy; map<int,multiset<int> > yfindx; void each(){ xfindy.clear(); yfindx.clear(); for(int i=1;i<=n;++i){ int x,y; scanf("%d%d",&x,&y); if(xfindy.count(x)==0){ multiset<int> tmp={y}; xfindy.insert({x,tmp}); } else{ (*xfindy.find(x)).second.insert(y); } if(yfindx.count(y)==0){ multiset<int> tmp={x}; yfindx.insert({y,tmp}); } else{ (*yfindx.find(y)).second.insert(x); } } for(int i=1;i<=m;++i){ int sw,d; scanf("%d%d",&sw,&d); if(sw==0){ if(!xfindy.count(d)) printf("0\n"); else { printf("%d\n",(*xfindy.find(d)).second.size()); multiset<int> del=xfindy.find(d)->second; xfindy.erase(xfindy.find(d)); for(multiset<int>::iterator j=del.begin();j!=del.end();++j){ yfindx[*j].erase(d); } } } else{ if(!yfindx.count(d)) printf("0\n"); else { printf("%d\n",(*yfindx.find(d)).second.size()); multiset<int> del=yfindx.find(d)->second; yfindx.erase(yfindx.find(d)); for(multiset<int>::iterator j=del.begin();j!=del.end();++j){ xfindy[*j].erase(d); } } } } } int main(){ while(1){ scanf("%d%d",&n,&m); if(n==0&&m==0) return 0; each(); } return 0; }
|