• 2017-2018 ACM-ICPC, NEERC, Northern Subregional Contest D Dividing Marbles


    题目大意:

    给出一个$N(N <= 2^{22}$),$N$的二进制表示中1的个数不超过4.  一开始有一个集合$S = {N}$, 每次操作可以选择$nin S (n > 1)$, 将$n$拆成两个正整数$n_1$和$n_2$,$n = n1 + n2$, 然后令$S = {S setminus n} cup {n_1, n_2}$.  问最少多少次操作使得$S = { 1 }$.

    题解:

    考虑将这个过程倒过来,本质是让求一个最短的Brauer chain。考虑一个Brauer chain $a_0, a_1, dots a_k$, 其中$a_0 = 1$, $ a_k = N$. 首先类似快速幂随便构造一下可以得到一个长度为$s+t-2$的Brauer chain,$s$ 是$N$二进制位数,$t$是二进制表示下1的个数。 比如$N = (1110)_2, s = 4, t = 3$, 可以如下构造:${(1)_2, (10)_2, (100)_2, (1000)_2, (1100)_2, (1110)_2}$. 因此答案上界是$s+t-2$.

    进一步挖掘一下性质:

    $$s = lfloor log_{2}{n} floor + 1 , t le 4$$

    $$k le s + t - 2 le lfloor log_{2}{n} floor + 1 + 4 - 2 le log_{2}{n} + 3$$

    可以得到$ a_k ge  2^{k - 3}$,根据Brauer chain的性质,必须有$a_{i - 1} >= frac{a_i}{2}$. 从$a_k$倒着推回去可以推出任意$0 le i le k$, $a_i ge 2^{i - 3}$.

    爆搜所有满足这个性质且最后一项不超过$2^{22}$的Brauer chain。 发现本地大概要跑50s。

    然后想到没必要让$k le s + t - 2 $取等号,因为等号的情况可以直接构造。所以我们让爆搜的条件更加严格一点,$k < s + t - 2 $ 也就是说$ k le s + t - 3$. 重新顺着刚才的思路推导一遍得到更强的条件$ a_i ge  2^{i - 2}$。 然后爆搜就只要0.2s左右了。对于没有搜到的解,直接构造即可。

     

    代码:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 #define N 100010
     5 typedef long long LL;
     6 const int mod = 998244353; 
     7 const double EPS = 1e-12;
     8 
     9 int a[30];
    10 vector<int> ans[(1 << 22) + 5];
    11 
    12 void DFS(int k)
    13 {
    14     if (k >= 2 && a[k] < (1 << (k - 2))) return;
    15     if (k > 25) return;
    16 
    17     if (__builtin_popcount(a[k]) <= 4 && (ans[a[k]].size() == 0 || k < ans[a[k]].size()))
    18     {
    19         ans[a[k]].clear();
    20         ans[a[k]] = vector<int>(a, a + k + 1);
    21     }
    22     for (int i = 0; i <= k; ++i)
    23     {
    24         a[k + 1] = a[k] + a[i];
    25         if (a[k + 1] > (1 << 22)) break;
    26         DFS(k + 1);
    27     }
    28 }
    29 
    30 int main()
    31 {
    32     freopen("dividing.in", "r", stdin);
    33     freopen("dividing.out", "w", stdout);
    34 
    35     a[0] = 1;
    36     DFS(0);
    37     for (int i = 1; i <= (1 << 22); ++i)
    38     {
    39         if (__builtin_popcount(i) > 4 || ans[i].size() > 0) continue;
    40         
    41         int j;
    42         for (j = 0; (1 << j) <= i; ++j)
    43             ans[i].push_back(1 << j);    
    44         j--;
    45         int now = 1 << j;
    46         for (int k = 0; k < j; ++k)
    47         {
    48             if ((i >> k) & 1) 
    49             {
    50                 now |= 1 << k;
    51                 ans[i].push_back(now);
    52             }
    53         }
    54     }
    55 
    56     int T, d1, d2, d3, d4, n, k;
    57     scanf("%d", &T);
    58     while (T--)
    59     {
    60         scanf("%d %d %d %d", &d1, &d2, &d3, &d4);
    61         n = (1 << d1) + (1 << d2) + (1 << d3) + (1 << d4);
    62         
    63         printf("%d
    ", k = ans[n].size() - 1);
    64         
    65         for (int i = k; i >= 1; --i)
    66         {
    67             printf("%d %d %d
    ", ans[n][i], ans[n][i - 1], ans[n][i] - ans[n][i - 1]);
    68         }
    69     }
    70     
    71     
    72     return 0;
    73 }
  • 相关阅读:
    leetcode 673. 最长递增子序列的个数 java
    leetcode 148. 排序链表 java
    leetcode 98. 验证二叉搜索树 java
    leetcode 29. 两数相除 java
    leetcode 234. 回文链表 java
    Valid Palindrome LeetCode Java
    Single Number II LeetCode Java
    Single Number LeetCode java
    Search in Rotated Sorted Array II LeetCode Java
    Search in Rotated Sorted Array leetcode java
  • 原文地址:https://www.cnblogs.com/vb4896/p/9489338.html
Copyright © 2020-2023  润新知