BZOJ.2118.墨墨的等式(思路 最短路Dijkstra 按余數分類)
阿新 • • 發佈:2018-09-26
個數 答案 lld while ems emp typedef 要求 int 要是滿足模\(a_i\)為\(x\)的最小的數(用\(n\)個數組合得到)。考慮如何對每個余數求這個\(d\)。
因為每次是枚舉\(n\)個數轉移(加上再取模),且狀態可以用數組存,要求最小,聯想到最短路。
\(dis[x]\)表示滿足模\(a_i\)為\(x\)的能組成的最小的數。那麽余數為\(0\)的最小的數自然是\(0\),即\(dis[0]=0\)。然後SPFA或Dijkstra。
題目鏈接
題意可以看做,用\(a_1,a_2,...,a_n\),能組成多少個\([L,R]\)中的數。
(40分就是個完全背包)
首先如果\(k*a_i+x\)可以組成,那麽\((k+1)*a_i+x\)自然也可以組成(\(k\geq 0,0\leq x<a_i\))。
即我們選取一個\(a_i\)做模數,若\(d\equiv x(mod\ a_i)\),則\(d+a_i\equiv x(mod\ a_i)\),那麽對於當前余數\(x\)我們可以算出\([0,R]\)中有多少個數模\(a_i\)為\(x\)(只能由\(d\)每次加\(a_i\))。對於所有余數算一次就能得到答案。
另外這個\(d\)
因為每次是枚舉\(n\)個數轉移(加上再取模),且狀態可以用數組存,要求最小,聯想到最短路。
\(dis[x]\)表示滿足模\(a_i\)為\(x\)的能組成的最小的數。那麽余數為\(0\)的最小的數自然是\(0\),即\(dis[0]=0\)。然後SPFA或Dijkstra。
至於\(a_i\),取最小的吧。這樣圖中的點數自然最少。(當然取哪個是任意的,因為最後統計答案時的\(a_i\)也是這個\(a_i\))
如果物品有下限限制,可以把邊界先減掉,再這麽求。
//9480kb 1792ms(慢啊→_→) #include <queue> #include <cstdio> #include <cctype> #include <cstring> #include <algorithm> #define gc() getchar() #define mp std::make_pair #define pr std::pair<LL,int> typedef long long LL; const int N=5e5+5; int n,mod,A[N]; LL dis[N]; std::priority_queue<pr> q; inline LL read() { LL now=0;register char c=gc(); for(;!isdigit(c);c=gc()); for(;isdigit(c);now=now*10+c-'0',c=gc()); return now; } void Dijkstra() { static bool vis[N]; memset(dis,0x3f,sizeof dis); dis[0]=0, q.push(mp(0,0)); while(!q.empty()) { int x=q.top().second; q.pop(); if(vis[x]) continue; vis[x]=1; for(int i=1,v; i<=n; ++i) if(dis[v=(x+A[i])%mod]>dis[x]+A[i]) q.push(mp(-(dis[v]=dis[x]+A[i]),v)); } } inline LL Calc(LL x) { LL ans=0; for(int i=0; i<mod; ++i) if(dis[i]<=x) ans+=(x-dis[i])/mod+1;//還有個dis[i] return ans; } int main() { n=read(); LL L=read(),R=read(); int cnt=0; mod=N; for(int i=1; i<=n; ++i) if(!(A[++cnt]=read())) --cnt;//忽略0 else mod=std::min(mod,A[cnt]); n=cnt, Dijkstra(); printf("%lld\n",Calc(R)-Calc(L-1)); return 0; }
BZOJ.2118.墨墨的等式(思路 最短路Dijkstra 按余數分類)