标签:code 需要 detail 最小 cpp tchar get 怎么 math
水博客太快乐了
虽然本蒟蒻很久之前就已经学过同余最短路了,但是当时并不完全懂,全是抄的代码。最近重新学了一下,发现其实很简单,完全不知道自己当时为什么不懂。。。
但是毕竟自己太水了,还是应该记录一下,万一以后又不会了该怎么办。。。
同余最短路便是应用于如那位巨佬所说乍一看就很神奇的题中。。。
它虽说是图论,却也可以被理解为一种对 \(dp\) 的优化。。。更神奇了。。。(然而这种理解我觉得并不好理解,因此不加以赘述)
然而这种题一般代码都简单的要死,而且有题目有一种特定的套路:
题目一般会给出几个数或一个数列,且数列长度很短,要求的是用数列中的数能拼出的数在某一范围的个数(即满足 \(b\in [l,r] , b= \sum_{i=1}^{n} x_{i}a_{i}\) 的个数),数列中每个数使用的次数不限,范围一般会很大,因此不能用背包 \(dp\) 。
解法通常是找出这个数列中最小的,即为 \(minn\) ,设 \(t\) 为小于 \(minn\) 的一个数,每个数显然都可以被表示成 \(k*minn+t\) ,并且若 \(k*minn+t\) 可以合法,那么 \((k+i)*minn+t\) 也一定合法。因此对于每个小于 \(minn\) 的数 \(t\) 只要求出最小的 \(k\) 便很容易求出给定范围中合法的数的个数。
求解的过程可以使用最短路,并且因为数据很难构造,所以 \(SPFA\) 在这里还没死。
看一道例题
正如前面所说,乍一看这个题目不知道该用什么算法,但如果你精通各种套路,那么就会知道这是一个同余最短路的板子题。。。
先找出 \(x,y,z\) 中最小的,这里就假设它是 \(x\) 好了。
然后对于每个小于 \(x\) 的 \(t\) ,分别连两条边 \((t,(t+y) \bmod x,y)\) , \((t,(t+z) \bmod x,z)\) ,然后以 \(1\) 为起点跑最短路即可(但是在实际跑的时候并不需要把图建出来)。
注意 \(t=0\) 也要算到答案里。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10, inf=0x7fffffffffffffff;
inline int read(){
int f=1, x=0; char ch=getchar();
while(!isdigit(ch)) { if(ch==‘-‘) f=-1; ch=getchar(); }
while(isdigit(ch)) { x=x*10+ch-48; ch=getchar(); }
return f*x;
}
int x, y, z, h;
int dis[N], a[4];
bool vis[N];
queue<int > q;
signed main(void){
h=read(), a[1]=read(), a[2]=read(), a[3]=read();
int minn=min(min(a[1], a[2]), a[3]);
if(minn==1) { printf("%lld\n",h); return 0; }
for(int i=0; i<minn; i++) dis[i]=inf;
dis[1]=1;
q.push(1);
while(!q.empty()){
int u=q.front(); vis[u]=0; q.pop();
for(int i=1;i<=3;i++){
int v=(u+a[i])%minn;
if(dis[v]>dis[u]+a[i]){
dis[v]=dis[u]+a[i];
if(!vis[v]) q.push(v), vis[v]=1;
}
}
}
int ans=0;
for(int i=0; i<minn; i++) if(dis[i]<=h) ans+=(h-dis[i])/minn+1;
printf("%lld\n",ans);
return 0;
}
再看一道练习题吧。
完全是套路题。。。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=20, inf=0x7fffffffffffffff, M=5e5+10;
inline int read(){
int f=1, x=0; char ch=getchar();
while(!isdigit(ch)) { if(ch==‘-‘) f=-1; ch=getchar(); }
while(isdigit(ch)) { x=x*10+ch-48; ch=getchar(); }
return f*x;
}
int n, l, r;
int a[N], dis[M];
bool vis[M];
queue<int> q;
signed main(void){
n=read(), l=read(), r=read();
for(int i=1; i<=n; ++i) a[i]=read();
sort(a+1, a+1+n);
for(int i=1; i<a[1]; ++i) dis[i]=inf;
q.push(0);
while(!q.empty()){
int u=q.front(); vis[u]=0; q.pop();
for(int i=1;i<=n;i++){
int v=(u+a[i])%a[1];
if(dis[v]>dis[u]+a[i]){
dis[v]=dis[u]+a[i];
if(!vis[v]) q.push(v), vis[v]=1;
}
}
}
int ans=0;
for(int i=0; i<a[1]; i++){
if(dis[i]==inf) continue;
dis[i]/=a[1];
ans+=max((r-i)/a[1]+1-max((l-1-i)/a[1]+1, dis[i]),0ll);
// printf("%lld %lld\n",dis[i],(r-i)/a[1]+1-max((l-1-i)/a[1]+1,dis[i]));
}
printf("%lld\n",ans);
return 0;
}
放下代码就溜了。。。
标签:code 需要 detail 最小 cpp tchar get 怎么 math
原文地址:https://www.cnblogs.com/CTcode/p/mod_spfa.html