同余最短路

同余最短路

一堆点凑成了 $\mathrm{sum}$, 则 $\mathrm{sum}$ 可以表示成 $\mathrm{k \times a[1] + i}$ 的形式.  

即 $\mathrm{k}$ 为商,后面的 $\mathrm{i}$ 为 $\mathrm{mod}$ $\mathrm{a[1]}$ 的余数.    

令 $\mathrm{dis[i]}$ 表示 $\mathrm{sum}$ 模 $\mathrm{a[1]}$ 等于 $\mathrm{i}$ 的情况下 $\mathrm{sum}$ 的最小值.   

通过 $\mathrm{dis[i]}$ 可以得到商,也就是 $\mathrm{k}$ 的最小值.   

通过 $\mathrm{k_{min}}$,可得到所有 $\mathrm{k' \times a[1]+i}$, $\mathrm{k_{min} \leqslant k'}$. 

由于后面的余数 $\mathrm{i}$ 不同,所以不重不漏统计到 $\mathrm{sum}$ 的所有可能取值.    

 

墨墨的等式

来源:洛谷P2371 [国家集训队]墨墨的等式    

同余最短路的模板题,统计 $[l,r]$ 区间内的答案时直接 $\mathrm{calc(r)-calc(l-1)}$ 即可.   

#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
#include <algorithm> 
#define N  500009 
#define ll long long 
#define pb push_back 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std; 
const ll inf=(ll)1e12;  
queue<int>q; 
int a[12],inq[N];   
ll dis[N];   
int main() {
    // setIO("input"); 
    int n ; 
    ll L, R;  
    scanf("%d%lld%lld",&n,&L,&R);  
    for(int i=1;i<=n;++i) scanf("%d",&a[i]);     
    sort(a+1,a+1+n);   
    int mx=a[1];   
    for(int i=0;i<mx;++i) dis[i]=inf;   
    dis[0]=0; 
    inq[0]=1; 
    q.push(0);   
    while(!q.empty()) {
        int u=q.front(); q.pop();  
        inq[u]=0;  
        for(int i=1;i<=n;++i) {
            int v = (u + a[i]) % mx;   
            if(dis[v] > dis[u] + a[i])  {
                dis[v] = dis[u] + a[i];   
                if(!inq[v]) {
                    q.push(v);  
                    inq[v] = 1;          
                }
            }
        }
    }       
    for(int i=0;i<mx;++i) {
        if(dis[i] == inf) continue;  
        dis[i] -= i; 
        dis[i] /= mx;  
    }
    ll ans=0;  
    for(int i=0;i<mx;++i) {
        ll pl = (L - 1 - i) / mx;  
        ll pr = (R - i) / mx;    
        ll t1 = max(0ll, pl - dis[i] + 1);    
        ll t2 = max(0ll, pr - dis[i] + 1);    
        ans += t2 - t1;  
    }
    printf("%lld\n", ans);  
    return 0; 
}

  

 

上一篇:学习笔记—KMP算法


下一篇:复变函数可视化-复积分