码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2014-08-18 20:36:12      阅读:400      评论:0      收藏:0      [点我收藏+]

标签:hdu   dp   数学   几何   想法   

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)

标签:hdu   dp   数学   几何   想法   

原文地址:http://blog.csdn.net/houserabbit/article/details/38664315

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!