2013 ACM/ICPC Asia Regional Online —— Warmup2

1004:Oh My Holy FFF

这题听人说是名次线段树,最后模仿别人的代码写的。

dp[i]=max{dp[j]-a[j])+a[i]*a[j]}  a[j]<a[i]&&i-l<=j<=i-1;

1.将所有的点的值排名,所以每次求当前点的时候,只求1-当前排名-1的这些点的最大值,把所有不可能的状态都设置为-INF。

2.因为每次长度都不能超过l,所以每次把i-l设置为-INF。并且更新当前点的名次。

注意下初始化问题:

1.当前点i的位置小于等于l的话,可以作为一段的最后一个点,可能直接取dp[i]=a[i]*a[i];

2.当前点的排名为1的话,如果不是第一个点,不可能作为一段的最后一个点,dp[i]=-INF;

代码:

#include<iostream>
#include<cstdio>
#include<vector>
#include<string>
#include<queue>
#include<cmath>
#include<algorithm>
#include<cstring>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define maxn 100005
#define INF 10000000005LL
#define mem(a,b) memset(a,b,sizeof(a))
#define FOR(i,s,t) for(int i=s;i<=t;i++)
#define ull unsigned long long
#define ll __int64
using namespace std;
int a[maxn],ord[maxn],rank[maxn];
ll dp[maxn];
ll max(ll x,ll y)
{
    return x>y?x:y;
}
bool cmp(int i,int j)
{
    if(a[i]==a[j]) return i>j;
    else return a[i]<a[j];
}
ll mx[maxn<<2];
void build(int l,int r,int rt)
{
    mx[rt]=-INF;
    if(l==r) return ;
    int m=(l+r)>>1;
    build(lson);
    build(rson);
}
void PushUp(int rt)
{
    mx[rt]=max(mx[rt<<1],mx[(rt<<1)|1]);
}
void updata(int p,ll val,int l,int r,int rt)
{
    if(l==r)
    {
        mx[rt]=val;
        return ;
    }
    int m=(l+r)>>1;
    if(p<=m)
    {
        updata(p,val,lson);
    }
    else
    {
        updata(p,val,rson);
    }
    PushUp(rt);
}
ll query(int L,int R,int l,int r,int rt)
{
    if(L<=l&&r<=R) return mx[rt];
    int m=(l+r)>>1;
    ll ret=-INF;
    if(L<=m)
    {
        ret=max(ret,query(L,R,lson));
    }
    if(R>m)
    {
        ret=max(ret,query(L,R,rson));
    }
    return ret;
}
int main()
{
    int t,tt=1;
    scanf("%d",&t);
    a[0]=0,dp[0]=0;
    while(t--)
    {
        int n,l;
        scanf("%d%d",&n,&l);
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
        }
        build(1,n,1);
        for(int i=1; i<=n; i++) ord[i]=i;
        sort(ord+1,ord+1+n,cmp);
        for(int i=1; i<=n; i++) rank[ord[i]]=i; //求出没个点的名次
        for(int i=1; i<=n; i++)
        {
            int r=rank[i]-1;
            ll tmp;
            if(i-l<1)//如果长度小于l,可以把该位置作为一段的最后一个位置
            {
                dp[i]=0;
            }
            else
            {
                dp[i]=-INF;
            }
            if(r<=0)//如果是第一小的数,肯定不能作为某组的最后一个数
            {
                tmp=-INF;
            }
            else
            {
                tmp=query(1,r,1,n,1);
            }
            dp[i]=max(dp[i],tmp);
            if(dp[i]==-INF)
                updata(rank[i],-INF,1,n,1);
            else
            {
                dp[i]+=(ll)a[i]*a[i];
                updata(rank[i],dp[i]-a[i],1,n,1);
            }
            if(i-l>0)
            {
                updata(rank[i-l],-INF,1,n,1);
            }
        }
        if(dp[n]==-INF)
            printf("Case #%d: No solution\n",tt++);
        else
            printf("Case #%d: %I64d\n",tt++,dp[n]);
    }
    return 0;
}


1010:The Shortest Path in Nya Graph 

hdu4725

这题一开始用spfa一直tle+wa,注意两点:

1.新增通过层来增加边,如果i属于第k层,一层分出入点和出点两个点,增加边i->n+(k-1)*2+1,n+(k-1)*2+2->i;

然后相邻层之间要增边,2*(k-1)+1->2*(k)+2,2*(k)+1->2*(k-1)+2;

2.spfa最坏的情况是O(m*n),这题m和n能达到10^5,果断会超。所以用优先级队列优化dijstra,能降到O(nlogn)。

代码:

#include<iostream>
#include<cstdio>
#include<vector>
#include<string>
#include<queue>
#include<cmath>
#include<algorithm>
#include<cstring>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define maxn 100005
#define INF 0x7fffffff
#define mem(a,b) memset(a,b,sizeof(a))
#define FOR(i,s,t) for(int i=s;i<=t;i++)
#define ull unsigned long long
#define ll long long
using namespace std;
int dis[3*maxn],vis[3*maxn];
int head[3*maxn];
struct node
{
    int v,w,nt;
} e[6*maxn];
struct point
{
    int d,u;
    bool operator< (const point& x)const
    {
        return d>x.d;//距离大的优先级小
    }
};
priority_queue<point> q;
int id;
void add(int u,int v,int w)
{
    e[id].v=v,e[id].w=w;
    e[id].nt=head[u];
    head[u]=id++;
}
void init(int n)
{
    id=0;
    memset(head,-1,sizeof(head));
    for(int i=1; i<=3*n; i++)
    {
        dis[i]=INF;
    }
    memset(vis,0,sizeof(vis));
}
void dij()
{
    dis[1]=0;
    q.push((point){0,1});
    while(!q.empty())
    {
        point tmp=q.top();q.pop();
        if(vis[tmp.u]) continue;
        vis[tmp.u]=1;
        for(int i=head[tmp.u];i!=-1;i=e[i].nt)
        {
            int v=e[i].v,w=e[i].w;
            if(dis[tmp.u]+w<dis[v])
            {
                dis[v]=dis[tmp.u]+w;
                q.push((point){dis[v],v});
            }
        }
    }
}
//void spfa()
//{
//    queue<int> q;
//    dis[1]=0;
//    q.push(1);
//    vis[1]=1;
//    while(!q.empty())
//    {
//        int u=q.front();
//        q.pop();
//        for(int i=head[u]; i!=-1; i=e[i].nt)
//        {
//            int v=e[i].v,w=e[i].w;
//            if(dis[u]+w<dis[v])
//            {
//                dis[v]=dis[u]+w;
//                if(!vis[v])
//                {
//                    vis[v]=1;
//                    q.push(v);
//                }
//            }
//        }
//        vis[u]=0;
//    }
//}
int main()
{
    int n,m,t,c,tt=1;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d",&n,&m,&c);
        init(n);
        int level,p1,p2,p;
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&level);
            p1=n+(level-1)*2+1,p2=p1+1;
            add(i,p1,0);
            add(p2,i,0);
        }
        int pp1,pp2;
        for(int i=1; i<n; i++)
        {
            p1=n+(i-1)*2+1,p2=p1+1;
            pp1=n+i*2+1,pp2=pp1+1;
            add(p1,pp2,c);
            add(pp1,p2,c);
        }
        int u,v,w;
        for(int i=0; i<m; i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w);
            add(v,u,w);
        }
        dij();
        if(dis[n]==INF)
            printf("Case #%d: %d\n",tt++,-1);
        else
            printf("Case #%d: %d\n",tt++,dis[n]);
    }
    return 0;
}

1007:Kia's Calculation

hdu4722

比较裸的数位dp吧,dp(i,j)表示长度为i位,数位和模10余j的个数。状态方程:dp(i,j)=sum(dp(i-1,10-k-j+10)%10) (0<=k<=9)

ans=sum(dp(i-1,10-m-k))  i从数位的总长度到0,如果是最后一位,0<=k<= a[i],否则0<=k<a[i];因为只有最后一位,才可以取到改位的数,高位取0的情况就是改位没有数。

代码:

#include<iostream>
#include<cstdio>
#include<vector>
#include<string>
#include<queue>
#include<cmath>
#include<algorithm>
#include<cstring>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define maxn 10005
#define INF 0xfffffff
#define mem(a,b) memset(a,b,sizeof(a))
#define FOR(i,s,t) for(int i=s;i<=t;i++)
#define ull unsigned long long
#define ll __int64
using namespace std;
int a[20];
ll f[20][20];
ll DP(int i,int j)
{
    if(i==0) return j==0?1:0;
    if(f[i][j]>=0) return f[i][j];
    ll &ret=f[i][j];
    ret=0;
    for(int k=0;k<10;k++)
    {
        ret+=DP(i-1,((j-k)%10+10)%10);
    }
    return ret;
}
ll count(ll x)
{
    if(x==0) return 1;
    if(x<0) return 0;
    int id=0;
    while(x)
    {
        a[id++]=x%10;
        x/=10;
    }
    ll ret=0,m=0;
    for(int i=id-1;i>=0;i--)
    {
        if(!i)
        {
            for(int j=0;j<=a[i];j++)
            {
                ret+=DP(i,((10-m-j)%10+10)%10);
            }
        }
        else
        {
            for(int j=0;j<a[i];j++)
            {
                ret+=DP(i,((10-m-j)%10+10)%10);
            }
        }
        m=(m+a[i])%10;
    }
    return ret;
}
void init()
{
    memset(f,-1,sizeof(f));
}
int main()
{
    int t;
    scanf("%d",&t);
    init();
    int tt=1;
    while(t--)
    {
        ll a,b;
        scanf("%I64d%I64d",&a,&b);
        printf("Case #%d: %I64d\n",tt++,count(b)-count(a-1));
    }
    return 0;
}


1011:Kia's calculation

给你两个数,任意位都可以随意调换,不能出现前置0.

用pre_permutation,next_permutation暴力肯定会超时,其实只要记录每个数字出现的次数,从9开始,找出所有的可能和模10为9的组合,然后该组合的数字减减。
hdu4726:

代码:

#include<iostream>
#include<cstdio>
#include<vector>
#include<string>
#include<queue>
#include<cmath>
#include<algorithm>
#include<cstring>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define maxn 1000005
#define INF 0xfffffff
#define mem(a,b) memset(a,b,sizeof(a))
#define FOR(i,s,t) for(int i=s;i<=t;i++)
#define ull unsigned long long
#define ll __int64
using namespace std;
char s1[maxn],s2[maxn],s[maxn];
int a1[10],a2[10];
int main()
{
    int t,tt=1;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%s",s1);
        scanf("%s",s2);
        printf("Case #%d: ",tt++);
        int l1=strlen(s1);
        memset(a1,0,sizeof(a1));
        int l2=strlen(s2);
        memset(a2,0,sizeof(a2));
        for(int i=0;i<l1;i++)
        {
            a1[s1[i]-'0']++;
        }
        for(int i=0;i<l2;i++)
        {
            a2[s2[i]-'0']++;
        }
        int flag=0,id=0,cnt=0;
        for(int sum=9;sum>0;sum--)
        {
            for(int i=9;i>=0;i--)
            {
                for(int j=9;j>=0;j--)
                {
                    if(a1[i]>0&&a2[j]>0&&(i+j)%10==sum&&i!=0&&j!=0)
                    {
                        a1[i]--;
                        a2[j]--;
                        s[id++]=char(sum+'0');
                        flag=1;
                        break;
                    }
                }
                if(flag==1) break;
            }
            if(flag==1) break;
        }
        if(flag==0)
        {
            printf("%d\n",(s1[0]-'0'+s2[0]-'0')%10);
            continue;
        }
        for(int sum=9;sum>=0;sum--)
        {
            for(int i=9;i>=0;i--)
            {
                for(int j=9;j>=0;j--)
                {
                    while((i+j)%10==sum&&a1[i]>0&&a2[j]>0)
                    {
                        a1[i]--;a2[j]--;
                        s[id++]=char(sum+'0');
                    }
                }
            }
        }
        s[id]='\0';
        printf("%s\n",s);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值