CF 552D. Vanya and Triangles 枚举+暴力

题意:二维平面上n个点,问从中选三个点能构成三角形的方案数?
n<=2e3,  -100<=x[i],y[i]<=100.


任选三个C(n,3) 扣除掉选三在同一条直线上的方案.

先枚举dx,dy 然后在选一个点作为起点 查看这条直线上有多少个点.
dx,dy要互质 否则一条直线会遍历多次.

注意以p[i]为起点的直线上过去有点被标记,那么要先扣除这v1个点方案 然后在加上新的点tot个的方案(为了维护一条直线上的最多点).O(m^2*nlogn)[m==100]

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e3+5,M=100;
ll n,x[N],y[N],cx[N],cy[N];
int mk[N][N];
ll C(ll n,ll m)
{
	if(n<m)
		return 0;
	return (n*(n-1)*(n-2))/6;
}
int gcd(int a,int b)
{
	return b==0?a:gcd(b,a%b);
}
struct node{
	int x,y;
}p[N];
bool vis[N];
bool cmp(node a,node b)
{
	if(a.x==b.x)
		return a.y<b.y;
	return a.x<b.x;
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>p[i].x>>p[i].y;
		p[i].x+=M,p[i].y+=M;
		cx[p[i].x]++,cy[p[i].y]++;
	}
	sort(p+1,p+1+n,cmp);
	for(int i=1;i<=n;i++)
		mk[p[i].x][p[i].y]=i;
	ll res=C(n,3);
	for(int dx=1;dx<=100;dx++)
	{
		for(int dy=1;dy<=100;dy++)
		{
			if(gcd(dx,dy)!=1)
				continue;
			memset(vis,0,sizeof(vis));
			for(int i=1;i<=n;i++)
			{
				if(vis[i])	continue;
				ll tot=0,x=p[i].x,y=p[i].y,v1=0;
				while(x<=200&&y<=200)
				{
					if(mk[x][y])
					{
						tot++;
						if(!vis[mk[x][y]])
							vis[mk[x][y]]=true;
						else
							v1++;
					}
					x+=dx,y+=dy;
				}
				res-=C(tot,3);
				res+=C(v1,3);
			}
		}
	}  
	for(int dx=1;dx<=100;dx++)
	{
		for(int dy=-100;dy<0;dy++)
		{
			if(gcd(dx,-dy)!=1)
				continue;
			memset(vis,0,sizeof(vis));
			for(int i=n;i>=1;i--)
			{
				if(vis[i])	continue;
				ll tot=0,x=p[i].x,y=p[i].y,v1=0;
				while(x<=200&&y>=0)
				{
					if(mk[x][y])
					{
						tot++;
						if(!vis[mk[x][y]])
							vis[mk[x][y]]=true;
						else
							v1++;
					}
					x+=dx,y+=dy;
				}
				res-=C(tot,3);
				res+=C(v1,3);
			}
		}
	}
	for(int i=0;i<=2*M+10;i++)
		res-=C(cx[i],3),res-=C(cy[i],3);
	cout<<res<<'\n';
	return 0;
}


标准解法:
可以先枚举一点p[i],剩下两个点随便选,然后在扣除掉选两个共线的情况.
现在想知道有多少个点和p[i]共线,
只要维护最简[gcd(dy,dx)==1]的二元组(dy,dx)的个数,遍历一遍点集即可 
注意合法的三元组(a,b,c) 在枚举a,b,c时都出现一次 所以最后答案要除以三 O(n^2logn).

#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> ii; 
typedef long long ll;
const int N=2e3+5,M=100;
ll n,x[N],y[N],cx[N],cy[N];
map<ii,int> mk;
int gcd(int a,int b)
{
	return b==0?a:gcd(b,a%b);
}
ll calc(ll x)
{
	if(x<2)
		return 0;
	return x*(x-1)/2;
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>x[i]>>y[i];
	ll res=0;
	for(int i=1;i<=n;i++)
	{
		mk.clear();
		for(int j=1;j<=n;j++)
		{
			if(i==j)	continue;
			int dx=x[j]-x[i],dy=y[j]-y[i];
			if(dx==0)
				dy=1;
			else if(dy==0)
				dx=1;
			else
			{
				int d=gcd(abs(dy),abs(dx));
				dy/=d,dx/=d;
				if(dy<0)
					dy=-dy,dx=-dx;			
			}
			mk[ii(dy,dx)]++; 
		}
		res+=(n-1)*(n-2)/2;//last two random
		for(auto it=mk.begin();it!=mk.end();it++)
			res-=calc(it->second);		
	}
	cout<<res/3<<'\n';
	return 0;
}





猜你喜欢

转载自blog.csdn.net/noone0/article/details/80053279