HDU 4945 2048
题意:给你一堆数字 问有几个子集可以拼出2048
思路:
拼数字的规则相当于让数字乘二 所以不是2^i的数字不会拼出2048 那么这些数可选可不选 即为2^cnt种可能
之后只要计算出有几个子集不可能拼出2048即可 不过简单的直接dp是2048*100000的复杂度的 会TLE
所以要变成先枚举元素 再枚举该种元素个数 再枚举2048种状态 然后利用组合求(组合里需要逆元)
为什么这样快? 因为比如枚举2^5这个元素的时候 最多取2^6个 枚举元素个数被缩小了
注意:题目比较卡时间 所以能少做取模运算就少做
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef __int64 LL; #define mod 998244353 #define N 2050 #define M 100010 int n,m,ans; int num[N],dp[15][N],two[M]; LL f[M],g[M]; inline void scan_d(int &ret) { char c; ret=0; while((c=getchar())<'0'||c>'9'); while(c>='0'&&c<='9') { ret=(ret<<3)+(ret<<1)+(c-'0'); c=getchar(); } } LL powmod(LL fm,int fn) { LL fans=1; while(fn) { if(fn&1) fans=(fans*fm)%mod; fn=fn>>1; fm=(fm*fm)%mod; } return fans; } int Min(int fa,int fb) { if(fa<fb) return fa; return fb; } int main() { int cas=1,i,j,k,kk,s,amt; two[0]=1; f[0]=1; for(i=1;i<M;i++) { two[i]=(two[i-1]<<1)%mod; f[i]=f[i-1]*i%mod; } g[0]=1; g[100000]=powmod(f[100000],mod-2); for(i=99999;i>=1;i--) g[i]=g[i+1]*(i+1)%mod; for(;;) { scan_d(n); if(!n) break; for(i=0;i<=11;i++) { num[two[i]]=0; for(j=0;j<2048;j++) dp[i][j]=0; } dp[0][0]=1; ans=0; for(i=1;i<=n;i++) { scan_d(k); num[k]++; } m=num[two[11]]; for(i=0;i<11;i++) { amt=num[two[i]]; m+=amt; s=Min(two[11-i],amt); for(j=0;j<=s;j++) { LL x=f[amt]*g[amt-j]%mod*g[j]%mod; for(k=(j<<i),kk=0;k<2048;k++,kk++) if(dp[i][kk]) { dp[i+1][k]+=dp[i][kk]*x%mod; if(dp[i+1][k]>=mod) dp[i+1][k]-=mod; } } } for(i=0;i<2048;i++) { ans+=dp[11][i]; if(ans>=mod) ans-=mod; } ans=(LL)(two[m]-ans+mod)%mod*two[n-m]%mod; printf("Case #%d: %d\n",cas++,ans); } return 0; }HDU 4946 Area of Mushroom
题意:平面上有许多个人 如果这个人到某个位置的距离严格小于其他人 则这个位置属于这个人 问有几个人拥有的范围是无穷的
思路:
首先只有速度最大的人才可能拥有无穷 因为速度稍小的人总会被速度大的人追上
其次只有凸包上的人才可能无穷 这个用极限可以证明 无穷远处凸包上的点一定会超过凸包内点
最后重合位置且速度都为最大的人一定什么都不拥有
注意:普通凸包模版算出来的凸包忽略的边上的点 要特判一下边上的点
#include<cstdio> #include<cstring> #include<cmath> #include<iostream> #include<algorithm> using namespace std; double eps = 1e-8; double pi = acos((double) (-1)); inline int dcmp(double a) //判断一个double型的符号 { if (fabs(a) < eps) return 0; if (a > 0) return 1; else return -1; } struct point { double x, y,v; int id,mark; inline point(double _x = 0.0, double _y = 0.0) { x = _x; y = _y; } inline point operator -(const point &b) const { return point(x - b.x, y - b.y); } inline point operator +(const point &b) const { return point(x + b.x, y + b.y); } inline point operator |(const double &b) const { return point(x * b, y * b); } inline double operator ^(const point &b) const { return x * b.y - y * b.x; } inline double operator *(const point &b) const { return x * b.x + y * b.y; } inline void input() { scanf("%lf%lf%lf", &x, &y, &v); mark=0; } }; inline bool operator ==(const point &a, const point &b) { return dcmp(a.x - b.x) == 0 && dcmp(a.y - b.y) == 0; } inline double len(point a) //向量的摸 { return sqrt(a * a); } inline double Angle(point a, point b) //两个共起点的向量的夹角! { double ans = acos((a * b) / len(a) / len(b)); return ans; } inline double jijiao(point v) { return atan2(v.y, v.x); } inline point rote(point a, double rad) //逆时针旋转rad弧度!! { return point(a.x * cos(rad) - a.y * sin(rad), a.x * sin(rad) + a.y * cos(rad)); } inline int isInteger(double xx) //判断一个double型数是否为整数!! { return dcmp(xx - int(xx + 0.05)) == 0; } inline double torad(double deg) //角度转换为弧度 { return deg / 180 * pi; } inline double dis(point a, point b) //两点之间的距离 { return sqrt((a - b) * (a - b)); } inline point getjiaodian(point p, point v, point q, point w) //前提有唯一交点!!参数方程,v,w都为方向向量,p,q,为两直线上的点,求交点 ! { point u; u = p - q; double t = (w ^ u) / (v ^ w); v.x = t * v.x; v.y = t * v.y; return p + v; } inline double dianToLine(point p, point a, point b) //点到直线的距离 { point v1 = b - a, v2 = p - a; return fabs((v1 ^ v2)) / len(v1); //不取绝对值为有向的!! } inline int isxiangjiao(point a, point b, point c, point d) //判断直线相交,重合,平行!!! { point aa, bb, cc, dd; aa = b - a; bb = d - c; if (dcmp(aa ^ bb) != 0) return 1; //相交 else { aa = a - d; bb = b - c; cc = a - c; dd = b - d; if (dcmp(aa ^ bb) != 0 || dcmp(cc ^ dd) != 0) return 2; //平行 else return 3; //重合 } } //************************************************** //求凸包!!! inline bool operator<(const point& A,const point& B){ return dcmp(A.x-B.x)<0||(dcmp(A.x-B.x)==0&&dcmp(A.y-B.y)<0); } inline bool mult(point b,point c,point a) { return dcmp((b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x))>=0; } inline int Graham(point pnt[], int n, point res[]){ int i, len, k = 0, top = 1; sort(pnt, pnt + n); if (n == 0) return 0; res[0] = pnt[0]; if (n == 1) return 1; res[1] = pnt[1]; if (n == 2) return 2; res[2] = pnt[2]; for (i = 2; i < n; i++) { while (top && mult(pnt[i], res[top], res[top-1])) top--; res[++top] = pnt[i]; } len = top; res[++top] = pnt[n - 2]; for (i = n - 3; i >= 0; i--) { while (top!=len && mult(pnt[i], res[top],res[top-1])) top--; res[++top] = pnt[i]; } return top; } //************************************************** point list[5000];//输入的点 ,下标从0开始!! point DD[5000];//输出凸包上的点,下标从0开始!! point f[5000]; int ans[5000]; int main() { int T, i, j; int n; int ca=0; while(~scanf("%d",&n)) { if(!n) break; ca++; memset(ans,0,sizeof(ans)); double mav=0; for(i=0;i<n;i++) { list[i].input(); list[i].id=i; mav=max(mav,list[i].v); } if(dcmp(mav)==0) { printf("Case #%d: ",ca); for(i=0;i<n;i++) printf("%d",ans[i]); printf("\n"); continue; } int cnt=0; for(i=0;i<n;i++) { if(dcmp(list[i].v-mav)==0) f[cnt++]=list[i]; } sort(f,f+cnt); for(i=0;i<cnt-1;i++) { if(f[i]==f[i+1]) { f[i].mark=1; f[i+1].mark=1; } } int top=Graham(f,cnt,DD); DD[top]=DD[0]; for(j=0;j<cnt;j++) { if(!f[j].mark) for(i=0;i<top;i++) { double aa=len(f[j]-DD[i]); double bb=len(f[j]-DD[i+1]); double cc=len(DD[i+1]-DD[i]); if(dcmp(cc-aa-bb)==0) { ans[f[j].id]=1; break; } } } printf("Case #%d: ",ca); for(i=0;i<n;i++) printf("%d",ans[i]); printf("\n"); } return 0; }HDU 4948 Kingdom
题意:图中任意两点之间有且仅有一条单向边 求一个顶点序列 使得每次新来一个点的时候 其他点到这个点的距离不超过2
思路:
反着思考 从原图里一个一个减去点 那么满足距离不超过2这个条件的情况下 每次一定减去入度最大点 证明题解里也比较详细 就是 如果入度最大为u点 到u距离为1的点集为w 距离为2的为y 假设v点与u点距离大于2 那么v一定不直接连向w 因此w必须连向v 同时u也必须连向v 那么v入度比u大 产生矛盾
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 510 int n; int ans[N],in[N]; char maz[N][N]; inline void scan_d(int &ret) { char c; ret=0; while((c=getchar())<'0'||c>'9'); while(c>='0'&&c<='9') { ret=(ret<<3)+(ret<<1)+(c-'0'); c=getchar(); } } int main() { int i,j,k,p; for(;;) { scan_d(n); if(!n) break; for(i=1;i<=n;i++) in[i]=0; for(i=1;i<=n;i++) { scanf("%s",maz[i]+1); for(j=1;j<=n;j++) { if(maz[i][j]=='1') in[j]++; } } for(i=1;i<=n;i++) { k=-1; for(j=1;j<=n;j++) { if(in[j]>k) { k=in[j]; p=j; } } in[p]=-1; ans[i]=p; for(j=1;j<=n;j++) { if(in[j]>0&&maz[p][j]=='1') in[j]--; } } for(i=n;i;i--) printf("%d%s",ans[i],(i!=1)?" ":"\n"); } return 0; }HDU 4950
题意: 略
思路:
水题 判断一下能不能打死 k下能不能打死 k+1下能不能打掉血即可
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef __int64 LL; LL h,a,b,k; int main() { int t=1; for(;;) { scanf("%I64d%I64d%I64d%I64d",&h,&a,&b,&k); if(!h&&!a&&!b&&!k) break; printf("Case #%d: ",t++); if(h<=a) puts("YES"); else if(a*k-b*(k-1)>=h) puts("YES"); else if(a*k>b*(k+1)) puts("YES"); else puts("NO"); } return 0; }HDU 4951 Multiplication table
题意:给你一张p进制乘法表 现在他把所有的数字都换掉 比如0变成1 3变成8… 问你 新表中0、1、2…分别变成了什么数字…
思路:
0可以特判 一行一列全一样 1也特判 十位全是0变成的数字
其他数字根据十位判断 因为p进制情况下 比如3*x 那么他的十位一定只有3中情况
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 510 int a[N][N][2],ans[N],vis[N]; int n; inline void scan_d(int &ret) { char c; ret=0; while((c=getchar())<'0'||c>'9'); while(c>='0'&&c<='9') { ret=(ret<<3)+(ret<<1)+(c-'0'); c=getchar(); } } int main() { int t=1,i,j,k,one,zero; for(;;) { scanf("%d",&n); if(!n) break; for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { scan_d(a[i][j][0]); scan_d(a[i][j][1]); } } for(i=1;i<=n;i++) { for(j=1;j<=n;j++) vis[a[i][j][0]]=1; for(j=0,k=0;j<n;j++) { k+=vis[j]; vis[j]=0; } if(k!=1) ans[k]=i; else { for(j=1;j<=n;j++) if(a[i][j][0]!=a[i][j][1]) break; if(j<=n) { ans[1]=i; one=i; } else { ans[0]=i; zero=a[i][1][0]; } } } printf("Case #%d: %d",t++,zero); for(i=1;i<n;i++) printf(" %d",a[ans[i]][one][1]); printf("\n"); } return 0; }HDU 4952 Number Transformation
题意:给你一个n 一个k 一共k个操作 第i次操作将n变成i的倍数中大于等于n的最小值
思路:
每次的n可以拆分成x*i 那么 x‘(i+1)>=xi 则 x‘>=x-x/(i+1) 我们发现x‘是递减的 所以总有尽头 因此暴力维护x‘ 最后乘k即可
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; __int64 x,y; int main() { int t=1,i; for(;;) { scanf("%I64d%I64d",&x,&y); if(!x&&!y) break; for(i=1;i<y;i++) { if(x<i+1) break; x-=x/(i+1); } printf("Case #%d: %I64d\n",t++,x*y); } return 0; }
2014多校联合八(HDU 4945 HDU 4946 HDU 4948 HDU 4950 HDU 4951 HDU 4952),布布扣,bubuko.com
2014多校联合八(HDU 4945 HDU 4946 HDU 4948 HDU 4950 HDU 4951 HDU 4952)
原文:http://blog.csdn.net/houserabbit/article/details/38664315