D123【模板】同余最短路 Dijkstra 算法 P2662 [WC2002] 牛场围栏

D123【模板】同余最短路 Dijkstra 算法 P2662 [WC2002] 牛场围栏_哔哩哔哩_bilibili

 

P2662 [WC2002] 牛场围栏 - 洛谷

给定 k 个整数,求这 k 个整数不能拼凑出的最大整数

思路

先找出这 $k$ 个整数中最小的整数 $x$,再同余建图 $add(i,(i+j)\%x,j),\; i\in [0,x-1],\; j\neq x$

如图为 $4,7,9$ 可拼凑出的整数,模 $4$ 的同余类:$\{4,8,12\},\{9,13,17,21\},\{14,18,22,26\},\{7,11,15,19\}$

同余类节点到 0 点的最短路相同:$d[0]=0,\;d[1]=9,\;d[2]=14,\;d[3]=7$

$d[i]$ 表示第 $i$ 类能拼出来的最小整数,即同余类的第一个数字 $4,9,14,7$

第 $i$ 类能拼出的整数为 $d[i],d[i]+x,d[i]+2x,...$,不能拼出的最大整数就是 $d[i]-x$

图中各类不能拼出的最大整数分别是 $0,5,10,3$。把所有的 $d[i]-x$ 求出来,取最大值就是答案

一种无解的情况是所给数字存在 $1$ 时,显然所有的整数都能拼出

另一种无解的情况是某个 $d[i]=\infty$,表示该类不能拼出最小整数,例如 $4,6$ 不存在余数为 $1$ 的类,$d[1]=\infty$

相关板子:

D122【模板】同余最短路 Dijkstra 算法 P3403 跳楼机 - 董晓 - 博客园

数论基础 - OI Wiki

 

// 同余最短路 Dijkstra 算法 O(MlogN)
#include<bits/stdc++.h>
#define int long long
#define pii pair<int,int>
using namespace std;

const int N=3005,M=3e6; //M=1500*1500
int idx,h[M],ww[M],to[M],ne[M];
void add(int x,int y,int z){
  to[++idx]=y;ww[idx]=z;ne[idx]=h[x];h[x]=idx;
}
int n,m;
int a[N],b[N];
int d[M],vis[M];
int ma=0,mi=3000;

void dijkstra(){
  for(int i=0;i<mi;i++) d[i]=1e18; 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]=true;
    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});
      }
    }
  }
}
signed main(){
  scanf("%lld%lld",&n,&m);
  for(int i=1,l;i<=n;i++){
    scanf("%lld",&l);
    mi=min(mi,l-m);
    ma=max(ma,l);
    for(int j=l-m;j<=l;j++) b[j]=1; //j是有用长度
  }
  if(mi<=1){puts("-1"); return 0;}
  for(int i=0;i<mi;i++)for(int j=mi;j<=ma;j++)
    if(b[j]==1) add(i,(i+j)%mi,j); //建图
  
  dijkstra();
  ma=*max_element(d,d+mi);
  if(ma==1e18) puts("-1");
  else printf("%lld\n",ma-mi);
}

 

posted @ 2026-03-30 20:30  董晓  阅读(115)  评论(0)    收藏  举报