D122【模板】同余最短路 Dijkstra 算法 P3403 跳楼机
D122【模板】同余最短路 Dijkstra 算法 P3403 跳楼机_哔哩哔哩_bilibili
给定 $x,y,z,h$,求出有多少 $k\in [1,h]$ 可以使等式 $ax+by+cz=k$ 存在非负整数解。
同余最短路问题形式:
1. 给定 n 个整数,求这 n 个整数能拼凑出多少的其他整数(n 个整数可以重复取)
2. 给定 n 个整数,求这 n 个整数不能拼凑出的最小(最大)的整数
3. 至少要拼几次才能拼出模 K 余 p 的数
例:$h=16,x,y,z=4,7,9$. 先把楼层总数和起点楼层向下偏移一层 $h=15$,从 0 层出发,可达楼层如图
可达楼层看做节点,$x,y,z$ 看做边权. 发现节点可以按模 $4$ 的余数分类:$\{4,8,12\},\{9,13\},\{14\},\{7,11,15\}$
并且同类节点到 0 点的最短路相同:$d[0]=0,\;d[1]=9,\;d[2]=14,\;d[3]=7$

同余最短路利用同余来构造一些状态(以便优化空间复杂度),把这些余数状态看作单源最短路中的点
连边:$i\overset{y}{\rightarrow} (i+y)\%x$, $add(i,(i+y)\%x,y)$ 例如 $add(3,(3+7)\%4,7)$,即 $add(3,2,7)$
状态转移:$d[(i+y)\% x]=d[i]+y$,其中 $d[i]$ 表示模 $x$ 的余数为 $i$ 时能到达的最小楼层
统计节点数:余数 0 的个数 $=\lfloor\frac{H}{4}\rfloor+1$,余数 3 的个数 $=\lfloor\frac{H-d[3]}{4}\rfloor+1$,所以 $ans=\sum (\lfloor\frac{H-d[i]}{x}\rfloor+1)$
其实,$x$ 不一定非要取三个数中最小的,任取一个做模都可以. 不过取最小的,状态点数会少,更好来
// 同余最短路 Dijkstra 算法 O(MlogN) #include<bits/stdc++.h> #define ll long long #define pli pair<ll,int> using namespace std; const int N=100010,M=N*2; const ll inf=(1ull<<63)-1; //long long 的最大值 ll h[M],idx,to[M],ne[M],ww[M]; void add(ll u,ll v,ll w){ to[++idx]=v;ww[idx]=w;ne[idx]=h[u];h[u]=idx; } ll H,x,y,z; ll d[N],vis[N]; void dijkstra(){ for(int i=0;i<x;i++) d[i]=inf; d[0]=0; priority_queue<pli,vector<pli>,greater<pli> > q; q.push({0,0}); while(!q.empty()){ int u=q.top().second; q.pop(); if(vis[u]) continue; vis[u]=1; for(int i=h[u]; i; i=ne[i]){ int v=to[i],w=ww[i]; if(d[v]>d[u]+w){ d[v]=d[u]+w; q.push({d[v],v}); } } } } int main(){ cin>>H>>x>>y>>z; for(int i=0; i<x; i++){ add(i,(i+y)%x,y); add(i,(i+z)%x,z); } dijkstra(); ll ans=0; --H; for(int i=0;i<x;i++) if(H>=d[i]) ans+=(H-d[i])/x+1; cout<<ans<<'\n'; }
给定 $n,a_{1…n},l,r$,求出有多少 $b∈[l,r]$ 可以使等式 $\sum_{i=1}^n a_ix_i=b$ 存在非负整数解。
思路
题目用 $n$ 个已知整数 $a_i(0\le a_i \le 5\times 10^5)$ 拼凑 $[l,r]$ 区间内的数,能拼出几个
拼凑区间内的数,用前缀和思想,拼凑 $[0,r]$ 内的个数 减去 拼凑 $[0,l-1]$ 内的个数
用上一题的同余最短路求解,注意本题的起点就是 0,从 0 点出发建图,$r,l$ 不用向下偏移 $1$
// 同余最短路 Dijkstra 算法 O(MlogN) #include<bits/stdc++.h> #define ll long long #define pii pair<ll,int> using namespace std; const int N=5e5+5,M=N*12; const ll inf=1e18; ll h[N],idx,to[M],ne[M],ww[M]; void add(ll u,ll v,ll w){ to[++idx]=v;ww[idx]=w;ne[idx]=h[u];h[u]=idx; } ll n,m,l,r,x,a[N]; ll d[N],vis[N]; void dijkstra(){ for(int i=0;i<x;++i)d[i]=inf; d[0]=0; priority_queue<pii,vector<pii>,greater<pii> > q; q.push({0,0}); while(!q.empty()){ int u=q.top().second; q.pop(); if(vis[u]) continue; vis[u]=1; for(int i=h[u]; i; i=ne[i]){ int v=to[i],w=ww[i]; if(d[v]>d[u]+w){ d[v]=d[u]+w; q.push({d[v],v}); } } } } int main(){ scanf("%lld%lld%lld",&n,&l,&r); for(int i=1;i<=n;++i){ scanf("%lld",&x); if(x) a[++m]=x; //x=0无用,去掉 } sort(a+1,a+m+1); x=a[1]; //a1做模,状态少 for(int i=0;i<x;++i)for(int j=2;j<=m;++j) add(i,(i+a[j])%x,a[j]); dijkstra(); ll ans=0; --l; for(int i=0;i<x;++i){ if(r>=d[i]) ans+=(r-d[i])/x+1; if(l>=d[i]) ans-=(l-d[i])/x+1; } printf("%lld\n",ans); }
浙公网安备 33010602011771号