您的位置:首页 > 其它

题解BZOJ-2118 图论 SPFA + SLE 最短路 数论

2017-03-24 16:02 295 查看
先来分析一下吧,我们在这些a里任取一个ai,表示为k,那么这个B%k肯定是在0–k-1之间的,如果一个B满足条件,这个B%k=d,那么(B+k)%k也肯定为d,那其实就是说,只要我们能找到,%k=d的,且满足条件的最小的B,在一直往上加k,直到加到r为止,能有多少个B,(这些B都是符合条件的),就得到了B%k=d所有的可能,在枚举不同的d,累加起来,不就是0–r内全部可能的B值了嘛。同理,0–l-1内所有可能的B值也可以求出,一减不就是l–r内的可能,不就是答案呐!

然后,之所以可以用SPFA,就是因为要求余d时最小的B,用dis[d]保存,首先队首是0,因为余0时,B为0是肯定可以且最小的(非负),所以dis[0]=0,通过加上不同的a,得到新的余数,如果得到相同的余数的话,可以用B较小的来更新did值,也就是“松弛”。



可以用SPFA + SLE优化

#include "queue"
#include "cstdio"
#include "cctype"
#include "cstring"

#define min(a, b)  ((a) < (b) ? (a) : (b))

template <class T>
inline bool readIn(T &x)  {
T flag = 1;  char ch;
while(!(isdigit(ch = (char) getchar())) && ch != EOF)  if( ch == '-' )  flag = -1;
if(ch == EOF)  return false;
for(x = ch - 48; isdigit(ch = (char) getchar()); x = (x << 1) + (x << 3) + ch - 48);
x *= flag;
return true;
}

template <class T>
inline void write(T x)  {
if (x > 9)
write(x / 10);
putchar(x % 10 + 48);
}

template <class T>
inline void writeIn(T x)  {
if (x < 0)  {
putchar('-');
x = -x;
}
write(x);
}

const int MAXN = (int) 5 * 1e5 + 5;

typedef long long LL;

int n, smin = 0x3f3f3f3f, a[13];
LL l, r, ansl, ansr, dis[MAXN];

inline void initialize()  {
readIn(n);readIn(l);readIn(r);
for(register int i = 1; i <= n; readIn(a[i]), (a[i] ^ 0) ? smin = min(a[i], smin) : smin, ++i);
}

class BellmanFord  {
private:
bool vis[MAXN];
std::deque<int> q;
inline void initialize()  {
q.clear();
std::fill(dis, dis + smin, (const LL) 1e12 + 5);
memset(vis, false, sizeof(int) * (n + 1));
}
public:
inline void SPFA(int s)  {
initialize();
dis[s] = 0, vis[s] = true;
q.push_front(s);
while( !q.empty() )  {
int u = q.front(); q.pop_front();
vis[u] = false;
for(register int i = 1; i <= n; ++i)  {
int v = ( u + a[i] ) % ::smin;
if( dis[v] > (LL) dis[u] + a[i] )  {
dis[v] = (LL) dis[u] + a[i];
if( !vis[v] )  {
if( q.empty() || dis[v] < dis[q.front()] )
q.push_front(v);
else  q.push_back(v);
vis[v] = true;
}
}
}
}
}
} SPFA;

int main()  {
initialize();
SPFA.SPFA(0);
for(register int i = 0; i < smin; ++i)  if(dis[i] <= r)  ansr += (r - dis[i]) / smin + 1;
for(register int i = 0; i < smin; ++i)  if(dis[i] <= l - 1)  ansl += ((l - 1) - dis[i]) / smin + 1;
writeIn(ansr - ansl);
putchar('\n');
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: