2014多校联合八(HDU 4945 HDU 4946 HDU 4948 HDU 4950 HDU 4951 HDU 4952)

来源:互联网 发布:c语言ri什么意思 编辑:程序博客网 时间:2024/05/16 19:25

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 100010int 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 510int 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 510int 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;}


0 0