HDU5738
题意就是要找一条直线上的点,注意有重点;
组合数公式从0取到n为2n,注意运用就好
C(0,N)+C(1,N)+....+C(N,N)=2N
C(R,N)+C(R+1,N)=C(R+1,N+1)
数点的时候每个点有两种情况(出现,不出现),如果与第i个点相同的有m个,那么对于只有相同点时,ans=pqow(2,m)-1【减去都消失的情况】;一次共线的有n个,ans =pqow(2,m)*(pqow(2,n)-1)。
注意运用第一个公式,因为第i个点必定要用到,其他的点用法有两种,只用i的重点和不止用i的重点
只用i的重点相当于从1开始取组合数
不止用i的重点则重点可以从0开始取组合数,非重点则从1开始取
#include <stdio.h> #include<algorithm> #include<string.h> #include<iostream> #include<map> using namespace std; typedef long long LL; map<pair<LL,LL>,int>vis; const int mod=1e9+7; int t,n; struct st{ LL x,y; }num[1005]; bool cmp(st a,st b){ if(a.x==b.x) return a.y<b.y; else return a.x<b.x; } LL gcd(LL a,LL b){ return b?gcd(b,a%b):a; } LL qpow(LL a,LL b){ if(b<0) return 0; a%=mod; LL ans = 1; while(b) { if(b&1) ans = (ans * a) % mod; b >>= 1; a = (a * a) % mod; } return ans; } int main() { freopen("in.txt","r",stdin); cin>>t; while(t--){ scanf("%d",&n); int i,j; for( i=1; i<=n; i++) scanf("%lld%lld",&num[i].x,&num[i].y); sort(num+1,num+1+n,cmp); LL ans=0; for( i=1; i<=n; i++){ vis.clear(); int res=1; for( j=i+1; j<=n; j++){ if(num[j].x==num[i].x&&num[i].y==num[j].y) res++; else{ LL dx=num[i].x-num[j].x; LL dy=num[i].y-num[j].y; LL gg=gcd(dx,dy); if(gg!=0){ dx/=gg; dy/=gg; } vis[make_pair(dx,dy)]++; } } if(res>1){ ans+=(qpow(2,res-1)-1)%mod;//第一种只用这个点的情况,用到当前遍历的点和与其重合的点 ans%=mod; } // cout<<ans<<" "<<i<<endl; for(map<pair<LL,LL>,int>::iterator it = vis.begin();it!=vis.end();it++) { LL cnt=(it->second); ans = (ans+((qpow(2,cnt)-1)*(qpow(2,res-1)))%mod)%mod;//用重点和其他点的情况 } } cout<<ans<<endl; } }