hdu 5582 Journey of Taku

题解参考:点击打开链接

首先如果Taku使用了第二种魔法,那么有K步的移动是可以确定的,于是我们记录每条边走完后, 通过第一种魔法下一步是哪条边,这样可以倍增算出K步后在哪儿,于是可以直接建图,求最短路即可。
重要的话一定要加粗说!**答案会爆int,另外题意有毒!**

倍增的是边:

#include <bits/stdc++.h>
using namespace std;
typedef pair<long long,int> pli;
const int maxm=200005;
const long long inf=0x3f3f3f3f3f3f3f3f;
int T,n,m,K,u[maxm],v[maxm],w[maxm],nxt[maxm],dp[maxm][20],magic[maxm];
long long d[maxm],cost[maxm][20],co[maxm];
priority_queue<pli,vector<pli>,greater<pli> > que;
struct node {
    int v,id,w;
    node(){}
    node(int v,int id,int w):v(v),id(id),w(w){}
    bool operator<(const node &rhs)const{
        if (w!=rhs.w)
            return w<rhs.w;
        return v<rhs.v;
    }
};
vector<node> G[maxm];
inline int getid(int u,int v,int w,int id=0,bool debug=false) {
    if (u==n)
        return m;
    int x=lower_bound(G[u].begin(),G[u].end(),node(0,v,w))-G[u].begin();
    if (v!=G[u][x].v)
        return G[u][x].id;
    if ((int)G[u].size()==1)
        return G[u][x].id;
    else {
        if (x>0&&x+1==(int)G[u].size())
            return G[u][x-1].id;
        if (x==0)
            return G[u][x+1].id;
        else {
            node t=G[u][x-1],s=G[u][x+1];
            t.w=abs(G[u][x].w-G[u][x-1].w);
            s.w=abs(G[u][x].w-G[u][x+1].w);
            if (debug) {
                printf("%d %d %d %d\n",t.id,t.w,s.id,s.w);
            }
            if (t<s)
                return t.id;
            else
                return s.id;
        }
    }
}
int main()
{
    //freopen("out.txt","w",stdout);
    scanf("%d",&T);
    for (int cas=1;cas<=T;++cas) {
        scanf("%d%d%d",&n,&m,&K);
        for (int i=1;i<=n;++i)
            G[i].clear();
        m<<=1;
        for (int i=0;i<m;i+=2) {
            scanf("%d%d%d",&u[i],&v[i],&w[i]);
            w[i+1]=w[i];
            u[i+1]=v[i];
            v[i+1]=u[i];
            G[u[i]].push_back(node(v[i],i,w[i]));
            G[v[i]].push_back(node(u[i],i+1,w[i]));
        }
        u[m]=v[m]=n;
        if (n==1) {
            printf("Case #%d: 0\n",cas);
            continue;
        }
        for (int i=1;i<=n;++i)
            sort(G[i].begin(),G[i].end());
        for (int i=0;i<m;++i) {
            nxt[i]=getid(v[i],u[i],w[i],i);
            dp[i][0]=nxt[i];
            cost[i][0]=(nxt[i]!=m);
        }
        cost[m][0]=0;
        dp[m][0]=m;
        magic[m]=nxt[m]=m;
        for (int i=1;i<20;++i) {
            dp[m][i]=m;
            cost[m][i]=0;
            for (int j=0;j<m;++j) {
                dp[j][i]=dp[dp[j][i-1]][i-1];
                cost[j][i]=cost[j][i-1]+cost[dp[j][i-1]][i-1];
            }
        }
        for (int i=0;i<m;++i) {
            magic[i]=i;
            co[i]=0;
            for (int j=0;j<20;++j)
                if ((K>>j)&1) {
                    co[i]+=cost[magic[i]][j];
                    magic[i]=dp[magic[i]][j];
                }
        }
        //for (int i=0;i<=m;++i)
        //    printf("i=%d nxti=%d magici=%d coi=%I64d\n",i,nxt[i],magic[i],co[i]);
        /*
        for (int j=0;j<2;++j)
            for (int i=0;i<=m;++i)
                printf("%d%c",dp[i][j],i==m?'\n':' ');
        for (int j=0;j<2;++j)
            for (int i=0;i<=m;++i)
                printf("%I64d%c",cost[i][j],i==m?'\n':' ');
        */
        memset(d,0x3f,sizeof d);
        int s=getid(1,0,0);
        d[s]=1;
        //printf("s=%d\n",s);
        que.push(pli(d[s],s));
        for (int i=0;i<(int)G[1].size();++i) {
            //printf("v=%d u=%d %I64d\n",v[G[1][i].id],u[G[1][i].id],co[G[1][i].id]);
            if (v[G[1][i].id]==n)
                d[m]=1;
            s=magic[G[1][i].id];
            if (co[G[1][i].id]<d[s]) {
                d[s]=co[G[1][i].id]+1;
                que.push(pli(d[s],s));
            }
        }
        while (!que.empty()) {
            int o=que.top().second;
            long long c=que.top().first;
            que.pop();
            if (c>d[o])
                continue;
            //printf("%d %d %I64d\n",u[o],v[o],c);
            int p=nxt[o];
            if (d[p]>d[o]+1) {
                d[p]=d[o]+1;
                que.push(pli(d[p],p));
            }
            int e=v[o];
            for (int i=0;i<(int)G[e].size();++i) {
                p=magic[G[e][i].id];
                if (d[p]>d[o]+co[G[e][i].id]+1) {
                    d[p]=d[o]+co[G[e][i].id]+1;
                    que.push(pli(d[p],p));
                }
            }
        }
        //for (int i=0;i<=m;++i)
        //    printf("%I64d%c",d[i],i==m?'\n':' ');
        long long res=inf;
        for (int i=0;i<=m;++i)
            if (v[i]==n)
                res=min(res,d[i]);
        printf("Case #%d: %I64d\n",cas,res==inf?-1:res);
    }
    return 0;
}
/*
1
9 10 5
1 7 42
2 5 19
5 8 3
3 4 78
4 7 59
8 1 27
6 3 88
8 3 17
4 9 46
6 8 15
*/
/*
5
5 4 100
1 2 1
2 3 3
2 4 10
4 5 10
3 2 2
1 2 1
1 3 4
5 5 1
1 2 1
2 3 2
3 4 3
2 4 4
4 5 5
5 6 1
1 2 1
2 3 1
3 4 1
4 5 1
5 2 1
5 1 1
5 6 1
1 2 1
2 5 1
1 3 1
3 4 1
4 5 1
2 3 1
*/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值