斩杀线计算大师
斩杀线计算大师
http://www.nowcoder.com/questionTerminal/87e4a81191134d45a9374a275d990ff1
这道题纯数学的做法都有点玄学,感觉要么就是时间复杂度有点问题要么就是正确性有些问题
下面给出同余最短路的做法
分析:
令,且
,对于一个
,存在非负整数
的条件显然为
且
将按照对
取模分类,可以发现,如果
,那么
,因为要满足上面两个条件才存在
,那么
越小越好;换言之,我们需要求出模c同余的p中最小的那个
做法:
令表示只用
和
能拼出的最小的
,且
,这样就有
个点,考虑对于点之间连边:
,边权为
,
,边权为
然后从0开始跑最短路就可以求出数组,枚举每一个
,如果满足
且
,此时就一定存在一个
,然后
也可以用扩展欧几里得定理解方程得到
时间复杂度为
#include<bits/stdc++.h> #define N 100005 using namespace std; typedef long long ll; ll a,b,c,k; ll dis[N]; bool vis[N]; struct Edge { int next,to; ll dis; }edge[N << 3]; int head[N],cnt; void add_edge(int from,int to,ll dis) { edge[++cnt].next = head[from]; edge[cnt].to = to; edge[cnt].dis = dis; head[from] = cnt; } void dijkstra(int s) { priority_queue< pair<ll,int> > q; memset(dis,100,sizeof(dis)); dis[s] = 0; q.push(make_pair(0,s)); while(!q.empty()) { int u = q.top().second; q.pop(); if(vis[u]) continue; vis[u] = 1; for(int i=head[u];i;i=edge[i].next) { int v = edge[i].to; if(dis[v] > dis[u] + edge[i].dis) { dis[v] = dis[u] + edge[i].dis; if(!vis[v]) q.push(make_pair(-dis[v],v)); } } } } ll exgcd(ll a,ll b,ll &x,ll &y) { if(!b) { x = 1; y = 0; return a; } ll d = exgcd(b,a % b,x,y); ll t = x; x = y; y = t - a / b * y; return d; } int main() { cin>>a>>b>>c>>k; for(int i=0;i<c;++i) { add_edge(i,(i + a) % c,a); add_edge(i,(i + b) % c,b); } dijkstra(0); for(int i=0;i<c;++i) { if(dis[i] <= k && (k - dis[i]) % c == 0) { ll x,y,z,d; d = exgcd(a,b,x,y); x = ((dis[i] / d) * x % (b / d) + (b / d)) % (b / d); y = (dis[i] - a * x) / b; z = (k - dis[i]) / c; cout<<x<<' '<<y<<' '<<z<<endl; break; } } return 0; }