zoukankan      html  css  js  c++  java
  • 【NOI 2018】屠龙勇士(扩欧)

    题意理解错了。。。

    一把剑打一条龙,打了$x$次后如果龙不死,你就Game Over了。

    显然,面对每条龙使用的剑是固定的,如果所有龙中有一条没打死你就挂了。

    可以知道,可行的答案集合就是所有龙的可行集合的交集。

    考虑当前面对第$i$条龙,若要打死它,$x$满足条件:$a_{i} - x * v_{i} + y * p_{i} = 0$,其中$a$是血量,$v$是攻击力,$p$是恢复力,$y$是某非负整数。

    可以把它转化为同余式:$a_{i} equiv x * v_{i} (mod ; p_{i})$。我们要找出满足该条件的$x$的最小正整数值。

    通常情况下,只要先算$v_{i}$在$(mod ; p_{i})$意义下的逆元即可,但此时两者不互质,不一定存在逆元,根据数论某定理:$ a * c equiv b * c (mod ; m) Rightarrow a equiv b (mod ; m / ( c , m ) $,我们先除去三者的$gcd$,再求逆元就可以了,如果此时仍然没有逆元,则无解。(这里有一个要注意的事情,当$p_{i}=1$的时候会出点问题,特判就可以了)

    可以发现,$x$可以的取值是一个等差数列,我们需要把$n$个等差数列合并起来,取它们的交集。

    假设我们即将合并的两个等差数列为$(a_{s}, d_{s}),(a_{i},d_{i})$,其中$a$为首项,$d$为公差。

    合并后的公差比较好算:$lcm(d_{s},d_{i})$。主要考虑如何求第一个数同时出现在两个等差数列中了。

    可以列出以下等式:$a_{s} + x * d_{s} = a_{i} + y * d_{i}$,其中$x, y$都是某个非负整数。

    移项转化为同余式:$x * d_{s} equiv a_{i} - a_{s} (mod ; d_{i})$,和刚刚的套路一样就可以解出$x$的值啦。

    最后显然$a_{s}$就是我们想要的答案。(提示:由于中间答案过大,使用中精度存储,也可以用快速乘)

    Update :

    求同余式$x * a equiv b (mod ; p)$的另一种思路,设$d = gcd(a, p)$,那有解当且仅当$d | b$。具体证明就是扩展欧几里得相关。

    那该同余方程等价于$x * frac{a}{d} equiv frac{b}{d} (mod ; frac{p}{d})$。此时由于$frac{b}{d}$一定与$frac{p}{d}$互质,通过求逆元直接解出$x$即可。

    $igodot$技巧&套路:

    • 基础数论,同余式$x * a equiv b (mod ; p)$求解的技巧。
    #include <cstdio>
    #include <set>
    
    typedef long long LL;
    const int N = 100005;
    
    int tc, n, m;
    LL ai[N], di[N], a[N], p[N], b[N], v[N];
    std::multiset<LL> S;
    
    inline LL Mul(LL a, LL b, LL p) {
      a %= p; b %= p;
      LL a0 = a & 1048575, a1 = a >> 20;
      return (((a1 * b % p) << 20) + a0 * b) % p;
    }
    LL Ex_gcd(LL a, LL b, LL &x, LL &y) {
      if (b == 0) return x = 1, y = 0, a;
      LL g = Ex_gcd(b, a % b, y, x);
      y -= a / b * x;
      return g;
    }
    
    inline LL Get(LL x, LL re = 0) {
      auto it = S.upper_bound(x);
      if (it != S.begin()) --it;
      re = *it; S.erase(it);
      return re;
    }
    
    int main() {
      scanf("%d", &tc);
      for (; tc; --tc) {
        S.clear();
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
        for (int i = 1; i <= n; ++i) scanf("%lld", &p[i]);
        for (int i = 1; i <= n; ++i) scanf("%lld", &b[i]);
        for (int i = 1; i <= m; ++i) scanf("%lld", &v[i]), S.insert(v[i]);
        
        LL as = 1, ps = 1;
        for (int i = 1; i <= n; ++i) {
          LL vi = Get(a[i]), x, y, ai, pi;
          LL d = Ex_gcd(vi, p[i], x, y);
          if (a[i] % d != 0) {
            as = -1; break;
          }
          x = (x % (p[i] / d) + p[i] / d) % (p[i] / d);
          ai = Mul(a[i] / d, x, p[i] / d);
          pi = p[i] / d;
          if (p[i] == 1) ai = a[i] / vi + (bool)(a[i] % vi);
          S.insert(b[i]);
          if (pi == 1) {
            if (as < ai) {
              LL bl = (ai - as) / ps + (bool)((ai - as) % ps);
              as = as + bl * ps;
            }
            continue;
          }
          LL dir = ((ai - as) % pi + pi) % pi;
          d = Ex_gcd(ps, pi, x, y);
          if (dir % d != 0) {
            as = -1; break;
          }
          x = (x % (pi / d) + pi / d) % (pi / d);
          LL nx = Mul(dir / d, x, pi / d);
          as = as + nx * ps;
          ps = ps / d * pi;
        }
        
        printf("%lld
    ", as);
      }
      
      return 0;
    }
    View Code
  • 相关阅读:
    03-高级选择器,属性选择器,伪类选择器
    02-css的选择器
    01- css引入方式
    HTML(三)
    HTML(二)
    Sublime text3 插件ColorPicker(调色板)不能使用快捷键的解决方法
    Sublime Text 3 快捷键汇总
    Sublime Text 3下载及常用插件安装
    box-shadow参数以及使用
    jquery.nav.js定位导航滚动插件
  • 原文地址:https://www.cnblogs.com/Dance-Of-Faith/p/9346231.html
Copyright © 2011-2022 走看看