zoukankan      html  css  js  c++  java
  • AT2384 [AGC015F] Kenus the Ancient Greek

    题目链接

    去年讲的题到今年还不会做,wtcl

    对于无序数对 ((x, y)),每次可进行 ((x,y) o (x, y mod x)) 的操作,定义 (F(x,y)) 表示将无序数对 ((x, y)) 操作到出现 0 的最小步数。当给定 (n,m) 时,定义 (mn = MIN {F(x,y) },1 le xle n, 1 le y le m,cnt = sum_{i=1}^n sum_{j=1}^m [F(x,y)=mn])。共 (3 imes 10^5) 组询问,每次给定 (n,m),求 (mn, cnt)

    我们可以构造一种 (F(x,y)) 最大的 ((x, y)),显然当 (x,y) 为斐波那契数列的相邻两项的时候 (F(x,y)) 会比较大(考虑反推,尽量让 (x,y) 小一些)。于是我们可以直接在斐波那契数列上二分找到合法的最大的 (F(x,y)),作为 (mn) 输出。

    现在的问题在于求 (cnt)。考虑到 (sum_{i=1}^n sum_{j=1}^m [F(x,y)=k]) 可能会有很多,但是很多是类似的,比如 (F(2,3),F(2,5),F(2,7),F(2,9),...) 都可以归为一类:((2,3+2t))。于是我们考虑用最小的这样的数对来计算所有的合法数对。方便起见,我们只考虑 (i le j)((i, j)) 数对,并规定“步数为 (k) 的类”为符合 (F(i,j)=k,i < j le 2i) 的所有数对。那么,我们可以打表求出步数为 (k) 的“类”都有什么:

    [(1,2) ]

    [(2,3),(3,4) ]

    [(3,5),(4,7),(5,7) ]

    [(5,8),(7,11),(7,12),(8,11) ]

    [... ]

    发现 (k) 类恰好有 (k) 个数对。并且发现第 (k) 类第 (i) 个数对恰好是第 (k-1) 类第 (i) 个数对的“反推”,最后一个可以由第 (k - 1) 类第 (1) 个数对转化来: ((i,j) o (i+j,2i+j))。于是直接预处理出所有“类”,每次 (O(log)) 回答询问。

    注意不要爆 long long

    关键代码:

    inline void work() {
      ll n, m, res = 0; read(n), read(m);
      if (n > m)  swap(n, m);
      if (n == 1) {
        printf("1 %lld
    ", m % P);
        return ;
      }
     
      int p;
      p = upper_bound(f + 1, f + 1 + ftot, n) - f - 1;
      if (f[p + 1] > m) --p;
      printf("%d ", p);
      if (p == 1) res = n % P;
      for (int i = 1; i <= p; ++i)
        if (pr[p][i].first <= n && pr[p][i].second <= m)
          res = (res + (m - pr[p][i].second) / pr[p][i].first + 1) % P;
      swap(n, m);
      for (int i = 1; i <= p; ++i)
        if (pr[p][i].first <= n && pr[p][i].second <= m)
          res = (res + (m - pr[p][i].second) / pr[p][i].first + 1) % P;
      printf("%lld
    ", (res % P + P) % P);
    }
    int main() {
      f[0] = f[1] = 1;
      for (int i = 2; f[i - 1] <= 3e18; ++i)  f[i] = f[i - 1] + f[i - 2], ftot = i;
      pr[1][1] = MP(1, 2);
      for (int i = 2; i <= 91; ++i) {
        for (int j = 1; j < i; ++j) pr[i][j] = MP(pr[i - 1][j].second, pr[i - 1][j].first + pr[i - 1][j].second);
        ll a = pr[i - 1][1].first, b = pr[i - 1][1].second;
        pr[i][i] = MP(a + b, a + a + b);
        for (int j = 1; j <= i; ++j) {
        	if (pr[i][j].first > 2e18)	pr[i][j].first = 2e18;
        	if (pr[i][j].second > 2e18)	pr[i][j].second = 2e18;
        }
      }
    }
    
  • 相关阅读:
    7,C++ public, protected, private 继承的区别
    6,又一道经典的微软题
    5,一道经典的微软程序题
    33. Search in Rotated Sorted Array
    198,House Robber
    4,二维数组中指针的问题
    剑指offer——stack与queue的互相实现
    3,C语言文件读写
    2,虚函数
    剑指offer——已知二叉树的先序和中序排列,重构二叉树
  • 原文地址:https://www.cnblogs.com/JiaZP/p/13671636.html
Copyright © 2011-2022 走看看