把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

qzezoj 1592 三角形计数

题面传送门
题目告诉我们,没有三点共线,所以我们可以容斥。
两条线,\(x,y\)相等,即平行。那么我们可以用总可能数-\(2\)条直线平行-\(3\)条直线平行。
\(hash\)来判断平行线。
代码实现:

#include<cstdio>
#include<cstring>
#include<cmath>
#define mod 600017
using namespace std;
long long n,ans,pus,flag[300039],tot[300039],f1[300039],f2[300039],z[300039],h[600039],g[300039],head,sx[300039],sy[300039],sz[300039];
inline void add(long long x,long long y) {
	long long now=(long long)abs(x*y)%mod,ss=h[now];
	while(ss) {if(f1[ss]==x&&f2[ss]==y) break;ss=z[ss];}
	if(!ss) f1[++head]=x,f2[head]=y,z[head]=h[now],h[now]=head,g[head]=1;
	else g[ss]++;
}
inline long long _hash(long long x,long long y) {
	long long now=(long long)abs(x*y)%mod,ss=h[now];
	while(ss) {if(f1[ss]==x&&f2[ss]==y) break;ss=z[ss];}
	if(!flag[ss]) {flag[ss]=1;return g[ss];}
	return 0;
}
inline void read(long long &x) {
	char s=getchar();int f=1;x=0;
	while(s<'0'||s>'9') {if(s=='-') f=-1;s=getchar();}
	while(s>='0'&&s<='9') x=(x<<3)+(x<<1)+(s^48),s=getchar();
	x*=f;
}
int main() {
	register int i;
	read(n);
	for(i=1; i<=n; i++) read(sx[i]),read(sy[i]),read(sz[i]),add(sx[i],sy[i]);
	ans=(n*(n-1)*(n-2)/6)%1000000007;
	for(i=1; i<=n; i++) {
		tot[i]=_hash(sx[i],sy[i]);
		if(tot[i]) {
			ans-=tot[i]*(tot[i]-1)*(tot[i]-2)/6%1000000007;
			if(ans<0) ans+=1000000007;
			ans-=tot[i]*(tot[i]-1)/2%1000000007*(n-tot[i])%1000000007;
			if(ans<0) ans+=1000000007;
		}
	}
	printf("%lld\n",ans);
}
posted @ 2020-04-09 13:09  275307894a  阅读(55)  评论(0)    收藏  举报
浏览器标题切换
浏览器标题切换end