http://acm.hdu.edu.cn/showproblem.php?pid=4380
http://acm.hdu.edu.cn/showproblem.php?pid=4353
http://acm.hdu.edu.cn/showproblem.php?pid=4367
昨天第9场比赛的第一题类型据说已经出成屎了,如果不是数据出错了,做就会被轮奸的。于是就坐了坐类似的题目。
这里有一个同一的前提,不存在三点共线;
首先给出这类题目的解法AC大神:http://hi.baidu.com/aekdycoin/item/3f151dafcfcfb9ac29ce9ddc
还有一个帮助理解s[i][j]的意思:http://www.mzry1992.com/blog/miao/2012-multi-university-training-contest-7.html
4380:
题意:
给定N个房子的坐标以及M个金石头的坐标,Farmer Greedy只能选择三个房子然后形成一个三角形,三角形内会包含金石头。Farmer Greedy比较喜欢奇数,问题:求形成的三角形中包含奇数个金石头的个数;
思路:
不同的暴力方法O(N^3*M)肯定会超时,所以要优化,上边AC大神给的解法已经说了优化方法,用O(n^2 + nlogn)的复杂度初始化,O(n^3)求解:
这里s[i][j]不是表示i->j“右边”的点的数量,而是上边给的那个连接的意思。getS( i,k,j)表示求i,j,k这个角度里面的点的数量。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define N 107 #define M 1007 #define ll __int64 #define inf 0x7f7f7f7f #define MOD 100000007 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<-----***------->") using namespace std; struct node { double x,y; }Nve[N],Mve[M]; int Right[N][N],s[N][N]; double save[N][N],angle[M]; int n,m; //寻找第一个大于等于val的坐标 int bsearch(double val) { int l = 0,r = m - 1; int mid = 0,ans = -1; while (l <= r) { mid = (l + r)/2; if (angle[mid] > val) r = mid - 1; else { l = mid + 1; ans = mid; } } return ans; } void init() { int i,j; int pos1,pos2; //求出任意两点的角度(-pi,pi] for (i = 0; i < n; ++i) { for (j = 0; j < n; ++j) { if (i == j) continue; save[i][j] = atan2((Nve[j].y - Nve[i].y), (Nve[j].x - Nve[i].x)); } } for (i = 0; i < n; ++i) { for (j = 0; j < m; ++j)//枚举i点以i点为中心按极角对m个点排序 { angle[j] = atan2((Mve[j].y - Nve[i].y), (Mve[j].x - Nve[i].x)); } sort(angle,angle + m); for (j = 0; j < n; ++j) { if (i == j) continue; double ang = save[i][j]; pos1 = bsearch(ang);//寻找i->j右边的第一个点也表示1-pos1的个数 s[i][j] = pos1; if (ang >= 0) { ang -= pi; pos2 = bsearch(ang); Right[i][j] = pos1 - pos2; } else { ang += pi; pos2 = bsearch(ang); Right[i][j] = m - (pos2 - pos1); } } } } int getS(int i,int k,int j)//求i,k,j这个角里面的点的个数 { int ang1 = save[k][i]; int ang2 = save[k][j]; if (ang1 > ang2) { if (ang1 - ang2 < pi) return (s[k][i] - s[k][j]); else return (m - (s[k][i] - s[k][j])); } else { if (ang2 - ang1 < pi) return s[k][j] - s[k][i]; else return (m - (s[k][j] - s[k][i])); } } int main() { //freopen("din.txt","r",stdin); int i,j,h; int cas = 1; while (~scanf("%d%d",&n,&m)) { for (i = 0; i < n; ++i) scanf("%lf%lf",&Nve[i].x,&Nve[i].y); for (i = 0; i < m; ++i) scanf("%lf%lf",&Mve[i].x,&Mve[i].y); init(); int ans = 0; for (i = 0; i < n; ++i) { for (j = i + 1; j < n; ++j) { for (h = j + 1; h < n; ++h) { int tmp = getS(i,j,h) + getS(j,h,i) + getS(h,i,j) + Right[i][j] + Right[j][h] + Right[h][i] - 2*m; //printf(">>%d\n",tmp); if (tmp&1) ans++; } } } printf("Case %d: %d\n",cas++,ans); } return 0; }
4353:
题意:给定n个点的坐标以及m个金矿的坐标,求在n个点里面选出若干点组成一个多边形是其A/B最小。A表示多边形的面积,B表示金矿的数目。
思路:
所以如上题,我们只枚举出三角形内点个数,然后除他的面积取最小即可,代码几乎一样。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a,b) (a) > (b)? (b):(a) #define Max(a,b) (a) > (b)? (a):(b) #define maxn 50004 #define N 207 #define M 507 #define ll __int64 #define inf 0x7f7f7f7f #define MOD 100000007 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) using namespace std; struct node { double x,y; }Nve[N],Mve[M]; int s[N][N],R[N][N]; double angle[M],save[N][N]; int n,m; double det(double x1,double y1,double x2,double y2) { return x1*y2 - x2*y1; } double cross(node a,node b,node c) { return det(b.x - a.x,b.y - a.y,c.x - a.x,c.y - a.y); } double getsum(double x1,double y1,double x2,double y2,double x3,double y3) { return iabs((x1*y2 - x2*y1 + x3*y1 - x1*y3 + x2*y3 - x3*y2))/2.0; } int bsearch(double ang) { int l = 1,r = m; int mid = 0,ans =0; while (l <= r) { mid = (l + r)>>1; if (ang < angle[mid]) r = mid - 1; else { l = mid + 1; ans = mid; } } return ans; } void init() { int i,j,pos1,pos2; for (i = 1; i <= n; ++i) { for (j = 1; j <= n; ++j) { if (i == j) continue; save[i][j] = atan2((Nve[j].y - Nve[i].y),(Nve[j].x - Nve[i].x)); } } for (i = 1; i <= n; ++i) { for (j = 1; j <= m; ++j) { angle[j] = atan2((Mve[j].y - Nve[i].y),(Mve[j].x - Nve[i].x)); } sort(angle + 1,angle + 1 + m); for (j = 1; j <= n; ++j) { if (i == j) continue; double ang = save[i][j]; pos1 = bsearch(ang); s[i][j] = pos1; if (ang > 0) { ang -= pi; pos2 = bsearch(ang); R[i][j] = pos1 - pos2; } else { ang += pi; pos2 = bsearch(ang); R[i][j] = m - (pos2 - pos1); } } } } int getS(int i,int k,int j) { double ang1 = save[k][i]; double ang2 = save[k][j]; if (ang1 > ang2) { if (ang1 - ang2 < pi) return s[k][i] - s[k][j]; else return (m - (s[k][i] - s[k][j])); } else { if (ang2 - ang1 < pi) return s[k][j] - s[k][i]; else return (m - (s[k][j] - s[k][i])); } } int main() { // freopen("din.txt","r",stdin); int t,cas = 1; int i,j,h; scanf("%d",&t); while (t--) { scanf("%d%d",&n,&m); for (i = 1; i <= n; ++i) scanf("%lf%lf",&Nve[i].x,&Nve[i].y); for (i = 1; i <= m; ++i) scanf("%lf%lf",&Mve[i].x,&Mve[i].y); init(); double ans = inf; for (i = 1; i <= n; ++i) { for (j = i + 1; j <= n; ++j) { for (h = j + 1; h <= n; ++h) { int B = 0; double are = cross(Nve[i],Nve[j],Nve[h]); int tmp = getS(i,h,j) + getS(h,j,i) + getS(j,i,h); if (are > 0)//正负不一样注意 B = tmp + R[i][j] + R[j][h] + R[h][i] - 2*m; else B = tmp + R[j][i] + R[h][j] + R[i][h] - 2*m; if (B != 0) { double A = getsum(Nve[i].x,Nve[i].y,Nve[j].x,Nve[j].y,Nve[h].x,Nve[h].y); // printf("%lf %d\n",A,B); ans = Min(ans,A/B); } } } } printf("Case #%d: ",cas++); if (ans == inf) printf("-1\n"); else printf("%.6lf\n",ans); } return 0; }
4367 :
题意:
给定n个点,然后AC首先去2个点组成线段ac,然后yayamao再在剩余的点里面取两个点,AC有n*(n-1)/2中可能,ki表示在i状态下yayamao取的两个点与i状态的线段相交可能数,求
注意这里是乘法。fib是斐波那契数列
思路:
这里:http://www.mzry1992.com/blog/miao/2012-multi-university-training-contest-7.html有详细的解题思路,就是和上边几乎一样,不过这里要用到了欧拉函数降幂。
这里多谢日化的讲解:
A^x = A^(x % Phi(C) + Phi(C)) (mod C) 这是最原始公式。。
A^x = (A^(x % Phi(C)) % C) * (A^Phi(C) % C) 这个就是把上面的式子的指数给拆开
当C为素数的时候 Phi(C) = C - 1, 所以 (A^Phi(C) % C) = 1, 费马小定理
所以当C为素数的时候A^x = A^(x % Phi(C)) % C
A^x = A^( x % (C - 1) ) % C
这里就把fib降了。。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a,b) (a) > (b)? (b):(a) #define Max(a,b) (a) > (b)? (a):(b) #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<---------------------->") #define ll __int64 #define inf 0x7f7f7f7f #define MOD 1000000007 #define maxn 40004 #define N 207 #define M 507 using namespace std; int n; struct node { double x,y; }p[N]; double save[N][N],angle[N]; int s[N][N],R[N][N],ct; ll f[N*N]; void initF() { f[0] = f[1] = 1; for (int i = 2; i <= 40000; ++i) f[i] = (f[i - 1] + f[i - 2])%(MOD - 1) + (MOD - 1);//降幂后的写法 } ll modexp(ll a,ll b) { ll res = 1; while (b) { if (b&1) res = res*a%MOD; a = a*a%MOD; b >>= 1; } return res; } double det(double x1,double y1,double x2,double y2) { return x1*y2 - x2*y1; } double cross(node a,node b,node c) { return det(b.x - a.x,b.y - a.y,c.x - a.x,c.y - a.y); } int bsearch(double val) { int l = 1; int r = ct; int ans = 0; while (l <= r) { int m = (l + r)>>1; if (val >= angle[m]) { l = m + 1; ans = m; } else r = m - 1; } return ans; } void init() { int i,j; int pos1,pos2; for (i = 1; i <= n; ++i) { for (j = 1; j <= n; ++j) { if (i == j) continue; save[i][j] = atan2((p[j].y - p[i].y),(p[j].x - p[i].x)); } } for (i = 1; i <= n; ++i) { ct = 0; for (j = 1; j <= n; ++j) { if (i == j) continue; angle[++ct] = atan2((p[j].y - p[i].y),(p[j].x - p[i].x)); } sort(angle + 1,angle + 1 + ct); for (j = 1; j <= n; ++j) { if (i == j) continue; double ang = save[i][j]; pos1 = bsearch(ang) - 1; s[i][j] = pos1; if (ang >= 0) { ang -= pi; pos2 = bsearch(ang); R[i][j] = pos1 - pos2; } else { ang += pi; pos2 = bsearch(ang); R[i][j] = n - 1 - (pos2 - pos1); } // printf(">>%d\n",R[i][j]); } } } int getS(int i,int k,int j) { double ang1 = save[k][i]; double ang2 = save[k][j]; if (ang1 > ang2) { if (ang1 - ang2 < pi) return (s[k][i] - s[k][j] - 1); else return (n - 3 - (s[k][i] - s[k][j] - 1)); } else { if (ang2 - ang1 < pi) return (s[k][j] - s[k][i] - 1); else return (n - 3 - (s[k][j] - s[k][i] - 1)); } } int main() { //freopen("din.txt","r",stdin); initF(); int i,j,h; while (~scanf("%d",&n)) { for (i = 1; i <= n; ++i) scanf("%lf%lf",&p[i].x,&p[i].y); init(); ll sum = 1; for (i = 1; i <= n; ++i) { for (j = i + 1; j <= n; ++j) { int ans = 0; for (h = 1; h <= n; ++h) { if (h == i || h == j) continue; double are = cross(p[i],p[j],p[h]); if (are < 0) continue;//以一个方向为据准 int tmp = getS(i,j,h) + getS(j,h,i) + getS(h,i,j) + R[i][j] + R[j][h] + R[h][i] - 2*(n - 3);//求三角形面积 ans += (getS(i,h,j) - tmp);//i,h,j角度里的面积减去三角形面积 if (ans >= MOD) ans %= MOD; } sum *= (modexp(ans,f[ans])+ 1); if (sum >= MOD) sum %= MOD; } } printf("%I64d\n",sum); } return 0; }