[BZOJ 2118] 墨墨的等式
Link:
Solution:
一眼望过去是数论题,结果是最短路经典模型???
从一个很基础的性质出发:
由$a\equiv b(\mod c)$,得$(a+c*k)\equiv b(\mod c)$
设$a[1]$为$<a_n>$中的最小值,$dist[i]$为$\mod a[1]=i$的最小数
因为如果$X$合法,则$X+a[1]*k$一定合法。
因此我们只要找到由$<a_n>$组成,$\mod a[1]$分别为$0,1,2......a[1]-1$的最小的数,
就等于找到了${<a_n>}$能构成数的集合(选择$a[1]$是为了保证余数数量尽可能少)
这样,我们只要计算对于每个$i$,有多少个$dist[i]+k*a[1]$在$[L,R]$内即可
而求解$dist[i]$就可以交给最短路算法了(建边$<i,(i+a[j])\mod a[1],a[j]>$)
Code:
//by NewErA #include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> P; const int MAXN=5e5+5; ll dat[MAXN],n,L,R,dist[MAXN]; vector<P> G[MAXN]; void dijkastra() { priority_queue<P,vector<P>,greater<P> > Q; for(int i=0;i<MAXN;i++) dist[i]=(ll)1e60;dist[0]=0; Q.push(P(0,0)); while(!Q.empty()) { int t=Q.top().second;Q.pop(); for(int i=0;i<G[t].size();i++) { int v=G[t][i].first; if(dist[v]>dist[t]+G[t][i].second) dist[v]=dist[t]+G[t][i].second,Q.push(P(dist[v],v)); } } } int main() { cin >> n >> L >> R; for(int i=1;i<=n;i++) cin >> dat[i]; sort(dat+1,dat+n+1); for(int i=0;i<dat[1];i++) //计算dist for(int j=2;j<=n;j++) G[i].push_back(P((i+dat[j])%dat[1],dat[j])); dijkastra(); ll res=0; for(int i=0;i<dat[1];i++) //统计答案 { if(dist[i]<=R) { ll l=max((ll)0,(L-dist[i])/dat[1]); if(l*dat[1]+dist[i]<L) l++; ll r=(R-dist[i])/dat[1]; if(r*dat[1]+dist[i]>R) r--; res+=(r-l+1); } } cout << res; return 0; }
Review:
这里的同余思想值得借鉴,
如果能找到符合条件的模式,可通过同余的方式来简化方案数(变为余数个数)