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;
}