1. 程式人生 > >[2019.2.28]BZOJ2118 墨墨的等式

[2019.2.28]BZOJ2118 墨墨的等式

min floor 取值 ans bit 如果 -a esp can

我們設最小的系數為\(m\)

那麽如果\(B=im+j\)(\(i,j\)為非負整數,\(j<m\))有整數解,那麽\(B=(i+k)m+j\)(\(k\)為正整數)也有整數解。我們只需要把\(B=im+j\)的整數解中系數\(m\)對應的未知數加\(k\)就好了。

所以我們記\(d_i\)表示\(B\)\(mod\ m\)意義下同余\(i\),並且這個\(B\)有解的最小的\(B\)

顯然\(d_0=0\)

以及我們可以用\(d_i+a_j\)更新\(d_{(i+a_j)mod\ m}\)

那麽可以理解為對於任意\(0\le i<m\)\(1\le j\le n\),節點\(i\)

到節點\((i+a_j)mod\ m\)有一條權值為\(a_j\)的單向邊。那麽\(d_i\)就是節點0到節點\(i\)的最短路。

所以我們之所以選擇最小的系數作為\(m\),是為了減少狀態數。

跑一遍玄學的SPFA就能夠處理出\(d\)了。

那麽在\(mod\ m\)意義下和\(i\)同余且小於等於\(x\)的合法的\(B\)的取值數量為\(\lfloor\frac{x-d_i+m}{m}\rfloor\)

於是我們設\(0\le B\le x\)時,使得方程有非負整數解的\(B\)取值的數量為\(ans(x)\),則\(ans(x)=\sum_{i=0}^{m-1}\lfloor\frac{max(x-d_i+m,0)}{m}\rfloor\)

題目所求即為\(ans(BMax)-ans(BMin-1)\)

code:

#include<bits/stdc++.h>
using namespace std;
const long long INF=1e13;
struct edge{
    int t,v,nxt;
}e[6000010];
int n,a[15],mn=1e9,cnt,be[500010],t,vis[500010];
long long bl,br,dis[500010],tot;
queue<int>q;
void add(int x,int y,int val){
    e[++cnt].t=y,e[cnt].v=val,e[cnt].nxt=be[x],be[x]=cnt;
}
void SPFA(){
    for(int i=1;i<mn;++i)dis[i]=INF;
    q.push(0);
    while(!q.empty()){
        t=q.front(),q.pop(),vis[t]=0;
        for(int i=be[t];i;i=e[i].nxt)dis[e[i].t]>dis[t]+e[i].v?dis[e[i].t]=dis[t]+e[i].v,(!vis[e[i].t]?q.push(e[i].t),vis[e[i].t]=1:0):0;
    }
}
long long ANS(long long x){
    tot=0;
    for(int i=0;i<mn;++i)tot+=max(0ll,x-dis[i]+mn)/mn;
    return tot;
}
int main(){
    scanf("%d%lld%lld",&n,&bl,&br);
    for(int i=1;i<=n;++i)scanf("%d",&a[i]),mn=min(mn*1ll,a[i]?a[i]*1ll:INF);
    for(int i=0;i<mn;++i)for(int j=1;j<=n;++j)add(i,(i+a[j])%mn,a[j]);
    SPFA();
    printf("%lld",ANS(br)-ANS(bl-1));
    return 0;
}

[2019.2.28]BZOJ2118 墨墨的等式