2021GDCPC广东省大学生程序设计竞赛 H.History

题意:
给定一个由n个点,m条边组成的图,每走一步,所有边权都会按以下公式变换
在这里插入图片描述

求1到n的最短路

思路:
如果每次都变换成不同的,namo这题感觉不太可做,但连续代入几次后发现,这是个周期为4的循环,故可以用分层图做

我们可以一开始建好四层图,也可以先存边在dij函数内求不同层之间的距离,这里我用了第二种方法

Code:

#include <bits/stdc++.h>
// #define debug freopen("_in.txt", "r", stdin);
#define debug freopen("_in.txt", "r", stdin), freopen("_out.txt", "w", stdout);
typedef long long ll;
typedef unsigned long long ull;
typedef struct Node *bintree;
using namespace std;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll maxn = 1e6 + 10;
const ll maxm = 6e6 + 10;
const ll mod = 998244353;
const double pi = acos(-1);
const double eps = 1e-8;
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1

ll T, n, m, p;
ll dis[maxn][5],vis[5][maxn];
vector<pair<ll,ll> > to[maxn];


ll qpow(ll a, ll b, ll MOD)
{
    ll ans = 1, base = a;
    while (b != 0)
    {
        if (b & 1)
            ans = ans % MOD * (base % MOD)%MOD;
        base = (base % MOD)%MOD * (base % MOD)%MOD;
        b >>= 1;
    }
    return ans % MOD;
}



ll cal(ll pos,ll x)
{
    ll dis=x;
    for(ll i=0;i<pos;i++)
    {
        dis=1ll*(1+dis)*qpow(((1-dis)%p+p)%p,p-2,p)%p;
    }
    return dis;
}

struct Node
{
    ll dis,u,pos;
    bool operator<(Node other)const
    {
        return dis>other.dis;
    }
};



void dij()
{
    priority_queue<Node>que;
    memset(dis,63,sizeof(dis));
    que.push({0,1,0});
    dis[1][0]=0;
    while (!que.empty())
    {
        auto temp=que.top();
        que.pop();
        ll u=temp.u;
        ll pos=temp.pos;
        if (vis[pos][u])
            continue;
        vis[pos][u] = 1;
        for (auto x: to[u])
        {
            ll v = x.first;
            ll w=x.second;
            ll npos=(pos+1)%4;
            ll dis1=cal(pos, w);
            if (dis[v][npos] > dis[u][pos] +dis1)
            {
                dis[v][npos] =dis[u][pos] +dis1;
                que.push({dis[v][npos],v,npos}); 
            }
        }
    }
}

void solve()
{
    scanf("%lld%lld%lld", &n, &m, &p);
    for (ll i = 1; i <= m; i++)
    {
        ll u,v,w;
        scanf("%lld%lld%lld",&u,&v,&w);
        to[u].push_back({v,w});
    }
    
    
    dij();
    ll minn=INF;
    for(ll i=0;i<4;i++)
    {
        minn=min(minn,dis[n][i]);
    }
    printf("%lld\n",minn);
}

signed main()
{
    T = 1;
    while (T--)
    {
        solve();
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值