BZOJ 2118 墨墨的等式 最短路 同餘類分析
一. 題目
墨墨突然對等式很感興趣,他正在研究
對於
二. 分析
求
即考慮一個數
首先的想法:
設
①邊界:
②遞推:
然後枚舉
然而
既然
方法①應該不行,考慮方法②。
注意到數據範圍
這樣的話,任何一個方案都屬於其中的一個類。
這樣 原問題 等價於 求滿足下列兩個條件的數的個數 :
①
②
我們假如能求出一個類中能湊出的數的個數,將
現在只用考慮求屬於一個類的能湊出的數的個數。
假如可以湊出值
對於每一個類,我們知道上界
這可以在
現在要求每一類中最小的能湊出的值。
這用SPFA,Dijsktra等算法皆可求出。
三. 代碼
(以前的代碼,懶得重新寫了)
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
typedef long long LL;
const int N=13;
const int M=500001;
const LL MAX=10000000000000;
int n; LL BMin,BMax;
int p[N],vtx;
LL dis[M]; int q[M],h,t,v[M];
inline LL query(LL w)
{
LL cnt=0;
for (int i=0;i<vtx;i++)
if (dis[i]<=w) cnt+=(w-dis[i])/vtx+1;
return cnt;
}
int main(void)
{
scanf("%d%lld%lld",&n,&BMin,&BMax);
for (int i=1;i<=n;i++) scanf("%d",&p[i]);
vtx=p[1]; for (int i=2;i<=n;i++) if (vtx>p[i]) vtx=p[i];
int now,nxt;
for (int i=1;i<vtx;i++) dis[i]=MAX;
v[q[t=1]=0]=1;
for (;h^t;)
{
v[now=q[h=h%vtx+1]]=0;
for (int r=1;r<=n;r++)
{
nxt=(dis[now]+p[r])%vtx;
if (dis[now]+p[r]<dis[nxt])
{
dis[nxt]=dis[now]+p[r];
if (!v[nxt])
{
v[nxt]=1;
q[t=t%vtx+1]=nxt;
}
}
}
}
printf("%lld\n",query(BMax)-query(BMin-1));
return 0;
}
四. 小結
1. 權值過大的調整
我們在使用基於權值的數組計算時,可能會應該目標太大而出現MLE和TLE的情況。
假如還是使用相同的方法,我們可以做如下幾種常見的調整:離散化,按權值大小分塊,同餘類分析。
2. 同餘類分析
當權值過大的時候,考慮使用這種方法。
這種問題求解的,通常是方案存在性問題和方案計數問題。
一般選取的同餘值