【离散化】【分类讨论】codeforces102055I Cockroaches

I. Cockroaches
time limit per test6.0 s
memory limit per test256 MB
inputstandard input
outputstandard output
There are N cockroaches in the field. Cockroach i is located at coordinate (xi,yi). No two cockroaches are located at the same spot. Boss Luo has a powerful pesticide that can instantly kill all cockroaches on the horizontal and vertical line of the spot where it is used. i.e. cockroaches with either the same x coordinate or y coordinate as the pesticide spot will be killed.

Boss Luo wonders how many cockroaches can be killed at most when the pesticide is used in one spot. He is also interested in the number of different subsets of the annihilated cockroaches when the pesticide kills most cockroaches.

Input
The first line of the input gives the number of test cases, T (1≤T≤100). T test cases follow.

For each test case, the first line contains an integers N (1≤N≤105), the number of cockroaches.

The next N lines each contains two integers x and y (1≤x,y≤109), describing the coordinates of the cockroaches.

For at least 80 test cases, it is guaranteed that N≤5000.

Output
For each test case, output one line containing “Case x: y z”, where x is the test case number (starting from 1), y is the maximum number of cockroaches that can be killed with pesticide applied on one spot, and z is the number of different subsets of the annihilated cockroaches when the pesticide kills most cockroaches.


 本题出自CCPC决赛。昨天和队友练习了这一场,切了五题,这题没时间打了。和B题有点像,都是那种没啥算法,但写起来有点膈应的那种。
 首先要分两种情况,一、行上蟑螂的最大值g加上列上蟑螂的最大值h,二,行上蟑螂的最大值加上列上蟑螂的最大值减去1。我们可以先去求具体是哪一种,而更优雅的方式是先假定是情况一,如果此基础上求出来第二个答案是0,那么就是情况二。所以问题全部落在求第二个答案上。
 所有的数据先离散化一下,然后搞vector,记录每行的蟑螂所在的列的蟑螂的值,这样的话。如果是情况一,那么就是枚举所有值为g的行,然后看值所有值为h的列,减去这一行里面vector里值为h的个数,因为这些个数是重复算了一次。
 如果是情况二,那么有两种子情况,第一种是行取g,而列取h,或者列取h-1,枚举所有值为g的行,减去这一行里面vector里值为h-1的个数,第二种是行取g-1,而列取h,枚举所有值为g-1的行,减去这一行里面vector里值为h的个数。求这些个数都是用二分查找,保证效率。
 还有两种特殊情况,因为题目里面要求的不是可选的格点数,而是杀灭的蟑螂不同集合数。所以如果蟑螂都在同行或者同列,答案就是n 1,另外是如果第一个答案是2,需要把第二个答案除2。
 这种分类一定要先想清楚再下手,要不然改来改去挺繁琐的。下面的代码没有仔细修改,可能做了一些不必要的操作。
 这题实现的细节还是比较有考验的。

#include<cstdio>
#include<unordered_map>
#include<vector>
#include<algorithm>
using namespace std;

struct item
{
	int d,pos;
	inline bool operator < (const item &t) const
	{
		return d<t.d;
	}
}P[200005],Q[200005];

int T,n,x[100005],y[100005],s[200005],sn,xx,yy,invx[200005],invy[200005],a1;
long long a2;
unordered_map<int,int> M;
vector<int> E[200005];

int main()
{
	scanf("%d",&T);
	for(int f=1;f<=T;f++)
	{
		M.clear();
		scanf("%d",&n);
		sn=0;
		for(int i=1;i<=n;i++)
			scanf("%d%d",&x[i],&y[i]),s[++sn]=x[i],s[++sn]=y[i];
		bool f1=false,f2=false;
		for(int i=1;i<=n&&!f1;i++)
			if(x[i]!=x[1])
				f1=true;
		for(int i=1;i<=n&&!f2;i++)
			if(y[i]!=y[1])
				f2=true;
		if(!f1||!f2)  //同行或同列特判
		{
			printf("Case %d: %d %d\n",f,n,1);
			continue;
		}
		sort(s+1,s+sn+1);
		for(int i=1;i<=sn;i++)
			E[i].clear();
		sn=unique(s+1,s+sn+1)-s-1; //离散化
		for(int i=1;i<=sn;i++)
			M[s[i]]=i,P[i].pos=Q[i].pos=i,P[i].d=Q[i].d=0;
		for(int i=1;i<=n;i++)
		{
			xx=M[x[i]],yy=M[y[i]];
			P[xx].d++,Q[yy].d++;
		}
		sort(P+1,P+sn+1);  //打乱行列,按顺序排列,为的是方便之后找同值的(后来发觉这么写更麻烦了一些,不排序做也OK的)
		sort(Q+1,Q+sn+1);
		for(int i=1;i<=sn;i++)
			invx[P[i].pos]=i,invy[Q[i].pos]=i;  //记一下原来的列对应现在的位置
		for(int i=1;i<=n;i++)
		{
			xx=M[x[i]],yy=M[y[i]];
			E[invx[xx]].push_back(Q[invy[yy]].d);
		}
		for(int i=1;i<=sn;i++)
			sort(E[i].begin(),E[i].end());
		int g=P[sn].d,h=Q[sn].d,li,lj;
		for(li=sn;P[li].d==g;li--);  //li+1到sn是所有值为g的行
		for(lj=sn;Q[lj].d==h;lj--);  //lj+1到sn是所有值为h的列
		a2=0;
		a1=g+h;
		for(int i=sn;i>li;i--)  //情况1
			a2+=sn-lj-(E[i].end()-lower_bound(E[i].begin(),E[i].end(),h));
		if(a2==0)
		{
			int ljj;
			a1=g+h-1;
			for(ljj=lj;ljj>0&&P[ljj].d==g-1;ljj--);  
			for(int i=sn;i>li;i--)  //情况2.1
			{
				auto tmp=equal_range(E[i].begin(),E[i].end(),h-1);
				a2+=sn-ljj-(tmp.second-tmp.first);
			}
			for(int i=li;i>0&&P[i].d==g-1;i--)  //情况2.2
				a2+=sn-lj-(E[i].end()-lower_bound(E[i].begin(),E[i].end(),h));
		}
		if(a1==2)
			a2/=2;
		printf("Case %d: %d %lld\n",f,a1,a2);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值