2016"百度之星" - 初赛(Astar Round2A)

本文解析了六道ACM竞赛编程题,包括全由特定数字组成的数列的模运算问题、数字排列组合以求最小相邻乘积和的问题、树形结构上的节点价值最大化路径问题、等差数列删除问题、特殊字符串中字符计数问题以及人员排序问题。

A.All X

题目:

http://acm.hdu.edu.cn/showproblem.php?pid=5690

题意:


F(x,m) 代表一个全是由数字x组成的m位数字。请计算,以下式子是否成立:
F(x,m) mod k ≡ c

思路:

k小于1e4,所以取模得到的结果不会多,直接暴力出循环节判断就行了。

代码:

//kopyh
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define MOD 1000000007
#define N 11234
using namespace std;
long long  n,m,sum,res,flag;
long long a[N];
int main()
{
    long long  i,j,k,cas,T,t,x,y,z,c;
    scanf("%I64d",&T);
    cas=0;
    while(T--)
    {
        scanf("%I64d%I64d%I64d%I64d",&x,&m,&k,&c);
        sum=0;
        t=x; y=1;
        while(t<k)t*=10,t+=x,y++;
        if(y>=m)
        {
            y=0;
            while(m--)y*=10,y+=x;
            printf("Case #%I64d:\n",++cas);
            printf(y%k==c?"Yes\n":"No\n");
            continue;
        }
        m-=y;m++;
        t%=k;
        memset(a,0,sizeof(a));
        while(!a[t])
        {
            a[t]=++sum;
            t*=10;t+=x;
            t%=k;
        }
        m%=sum;
        if(!m)m=sum;
        t=-1;
        for(i=0;i<N;i++)if(a[i]==m)t=i;
        printf("Case #%I64d:\n",++cas);
        printf(t==c?"Yes\n":"No\n");
    }
    return 0;
}

B.Sitting in Line

题目:

http://acm.hdu.edu.cn/showproblem.php?pid=5691

题意:


给出一列数字,其中有的数字的位置是不固定的,找到重新排列之后的相邻数字乘积之和最小的结果。

思路:

不超过16个数字,直接数位dp判断已用的数字,然后一位一位往后枚举就行了。

代码:

//kopyh
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
#define MOD 1000000007
#define N 16
using namespace std;
long long n,m,sum,res,flag;
long long a[N],b[N],dp[1<<16][16];
int main()
{
    long long i,j,k,cas,T,t,x,y,z;
    scanf("%d",&T);
    cas=0;
    while(T--)
    {
        scanf("%I64d",&n);
        for(i=0;i<n;i++)scanf("%I64d%I64d",&a[i],&b[i]);
        for(i=0;i<(1<<n);i++)for(j=0;j<n;j++)dp[i][j]=-INF;
        for(unsigned int i=0;i<(1<<n);i++)
            for(j=0;j<n;j++)
                if(dp[i][j]!=-INF||__builtin_popcount(i)==0)
                    for(k=0;k<n;k++)
                        if(!((1<<k)&i) && (b[k]==-1||b[k]==__builtin_popcount(i)))
                            dp[(1<<k)|i][k] = max(dp[(1<<k)|i][k],dp[i][j]!=-INF?dp[i][j]+a[j]*a[k]:0);
        res=-INF;
        for(i=0;i<n;i++)res=max(res,dp[(1<<n)-1][i]);
        printf("Case #%I64d:\n%I64d\n",++cas,res);
    }
    return 0;
}

C.Snacks

题目:

http://acm.hdu.edu.cn/showproblem.php?pid=5692

题意:


一棵树上每个节点都有价值,求从起点0经过某个节点的总价值和最大的路径,两种操作,0:改变一个节点的价值,1:找到经过某节点的最优路径

思路:

对于一棵树只要处理出起点到每个节点的价值和,然后每次询问就是找对应节点为根的子树中的最大值,修改就是对应子树全部变化,很明显直接dfs序然后预处理就行了。

代码:

//kopyh
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
#define MOD 1000000007
#define N 212345
using namespace std;
long long n,m,res,flag;
vector<long long>g[N];
long long val[N],sum[N];
long long a[N],l[N],r[N];
void init()
{
    for(int i=0;i<n;i++)g[i].clear();
}
#define root 1 , n , 1
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
struct node
{
    long long pos,val;
    node(long long x=0,long long y=0){pos=x,val=y;}
    friend bool operator < (node a, node b)
    {
        return a.val < b.val;
    }
}arr[N<<2];
long long add[N<<2],tot;
void pushUp(long long rt)
{
    arr[rt] = max(arr[rt<<1],arr[rt<<1|1]);
}
void pushDown(long long l,long long r,long long rt)
{
    if(add[rt])
    {
        long long m = (l+r)>>1;
        add[rt<<1] += add[rt];
        add[rt<<1|1] += add[rt];
        arr[rt<<1].val += add[rt];
        arr[rt<<1|1].val += add[rt];
        add[rt] = 0;
    }
}
void updata(long long l,long long r,long long rt,long long ql,long long qr,long long val)
{
    if(l>qr||ql>r)return;
    if(l>=ql&&r<=qr)
    {
        arr[rt].val += val;
        add[rt] += val;
        return;
    }
    pushDown(l,r,rt);
    long long m = (l+r)>>1;
    if(ql<=m)updata(lson,ql,qr,val);
    if(qr>m)updata(rson,ql,qr,val);
    pushUp(rt);
}
void build(long long l,long long r,long long rt)
{
    add[rt]=0;
    if(l == r)
    {
        arr[rt].val = sum[a[++tot]];
        arr[rt].pos = tot;
        return;
    }
    long long m = (l+r)>>1;
    build(lson);
    build(rson);
    pushUp(rt);
}
node query(long long l,long long r,long long rt,long long ql,long long qr)
{
    if(l>qr||ql>r)
        return node(-INF,-INF);
    if(l>=ql&&r<=qr)
        return arr[rt];
    pushDown(l,r,rt);
    long long m = (l+r)>>1;
    return max(query(lson,ql,qr),query(rson,ql,qr));
}
void dfs(long long pos, long long fa)
{
    a[++tot]=pos;
    l[pos]=tot+1;
    for(int i=0;i<g[pos].size();i++)
    {
        if(g[pos][i]==fa)continue;
        sum[g[pos][i]]=sum[pos]+val[g[pos][i]];
        dfs(g[pos][i],pos);
    }
    r[pos]=tot+1;
}
int main()
{
    long long i,j,k,cas,T,t,x,y,z;
    scanf("%I64d",&T);
    cas=0;
    while(T--)
    {
        printf("Case #%I64d:\n",++cas);
        scanf("%I64d%I64d",&n,&m);
        init();
        for(i=0;i<n-1;i++)
        {
            scanf("%I64d%I64d",&x,&y);
            g[x].push_back(y);
            g[y].push_back(x);
        }
        for(i=0;i<n;i++)scanf("%I64d",&val[i]);
        sum[0]=val[0];tot=-1;
        dfs(0,0);
        tot=-1;
        build(root);
        for(i=0;i<m;i++)
        {
            scanf("%I64d",&z);
            if(z)
            {
                scanf("%I64d",&x);
                printf("%I64d\n",query(root,l[x],r[x]).val);
            }
            else
            {
                scanf("%I64d%I64d",&x,&y);
                updata(root,l[x],r[x],y-val[x]);
                val[x]=y;
            }
        }
    }
    return 0;
}

D.Game

题目:

http://acm.hdu.edu.cn/showproblem.php?pid=5693

题意:


所有连续的等差数列并且差值在列表之中的能够删去,求删到最后最多能删去多少数。

思路:

典型区间dp,对于能够删去的数对预处理,然后区间dp跑就行了。

代码:

//kopyh
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define MOD 1000000007
#define N 1123
using namespace std;
int n,m,sum,res,flag;
int a[N],d[N],b[N][N],dp[N][N];
int main()
{
    int i,j,k,cas,T,t,x,y,z,len,l,r;
    scanf("%d",&T);
    cas=0;
    while(T--)
    {
        scanf("%d%d",&n,&m);
        memset(b,0,sizeof(b));
        memset(dp,0,sizeof(dp));
        for(i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for(i=1;i<=m;i++)
        {
            scanf("%d",&d[i]);
            for(j=1;j<=n;j++)
                for(k=j+1;k<=n;k++)
                    if(a[k]-a[j]==d[i])
                        b[j][k] = 1;
        }
        for(len=1;len<=n;len++)
            for(l=1;l<=n-len;l++)
            {
                r=l+len;
                dp[l][r]=max(dp[l][r-1],dp[l+1][r]);
                if(b[l][r]&&dp[l+1][r-1]==(r-l-1))
                    dp[l][r] = max(dp[l][r],dp[l+1][r-1]+2);
                for(i=l+1;i<r;i++)
                {
                    dp[l][r] = max(dp[l][r],dp[l][i]+dp[i+1][r]);
                    if(b[l][i]&&b[i][r]&&a[i]-a[l]==a[r]-a[i]&&dp[l+1][i-1]==(i-l-1)&&dp[i+1][r-1]==(r-i-1))
                        dp[l][r] = max(dp[l][r],r-l+1);
                }
            }
        printf("%d\n",dp[1][n]);
    }
    return 0;
}

E.BD String

题目:

http://acm.hdu.edu.cn/showproblem.php?pid=5694

题意:


S(1)=B
S(2)=BBD
S(3)=BBDBBDD

S(n)=S(n−1)+B+reverse(flip(S(n−1))
其中,reverse(s)指将字符串翻转,比如reverse(BBD)=DBB,flip(s)指将字符串中的B替换为D,D替换为B,比如flip(BBD)=DDB。
求上面描述的字符串在L到R直接B的个数。

思路:

明显每个串的前后半段基本一致,除了中间处前半段为B后半段为D,所以可以直接递归求解。

代码:

//kopyh
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define MOD 1000000007
#define N 61
using namespace std;
long long n,m,sum,res,flag;
long long b[N],d[N],len[N];
void init()
{
    b[1]=len[1]=1;d[1]=0;
    for(int i=2;i<N;i++)
        b[i]=b[i-1]+d[i-1]+1,d[i]=d[i-1]+b[i-1],len[i]=len[i-1]*2+1;
}
long long solve(long long n)
{
    if(n<=1)return n;
    long long t = lower_bound(len,len+N,n)-len;
    if(len[t]==n)return b[t];
    return b[t-1]+1+solve(n-len[t-1]-1)-(n>=(len[t-1]+1)/2+1+len[t-1]);
}
int main()
{
    init();
    long long i,j,k,cas,T,t,x,y,z;
    scanf("%I64d",&T);
    cas=0;
    while(T--)
    {
        scanf("%I64d%I64d",&x,&y);
        printf("%I64d\n",solve(y)-solve(x-1));
    }
    return 0;
}

F.Gym Class

题目:

http://acm.hdu.edu.cn/showproblem.php?pid=5695

题意:


所有人排序,有的人会不想让某人站在他前面,每个人的评分是他前面还包括他自己的所有人序号的最小值,求可能的最大评价之和。

思路:

只要满足拓扑序的前提下尽可能的让序号大的人在前面就行了。

代码:

//kopyh
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define MOD 1000000007
#define N 1123456
using namespace std;
int n,m,sum,res,flag;
vector<int>g[N];
priority_queue<int>q;
int a[N],b[N],tot;
int main()
{
    int i,j,k,cas,T,t,x,y,z;
    scanf("%d",&T);
    cas=0;
    while(T--)
    {
        scanf("%d%d",&n,&m);
        memset(a,0,sizeof(a));
        for(i=1;i<=n;i++)g[i].clear();
        tot=0;
        while(!q.empty())q.pop();
        for(i=0;i<m;i++)
        {
            scanf("%d%d",&x,&y);
            g[x].push_back(y);
            a[y]++;
        }
        for(i=1;i<=n;i++)if(!a[i])q.push(i);
        while(!q.empty())
        {
            t=q.top();q.pop();
            b[tot++]=t;
            for(i=0;i<g[t].size();i++)
            {
                a[g[t][i]]--;
                if(!a[g[t][i]])q.push(g[t][i]);
            }
        }
        long long sum=0;t=INF;
        for(i=0;i<n;i++)
        {
            t=min(t,b[i]);
            sum+=t;
        }
        printf("%I64d\n",sum);
    }
    return 0;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值