zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    CGO入门和OCR文字识别(非第三方API,有源码,效果好)实战
    Golang中如何正确的使用sarama包操作Kafka?
    音量强度转分贝db
    ShowDialog()弹出的窗体,关闭后,主窗体会闪烁的BUG
    小鱼提问3 static方法中可以访问某个类的私有变量吗(不通过反射的其他非正常手段)?什么情况下可以?
    “-="的陷阱
    c++ 从一个BYTE[] *filePtr 追加二进制文件
    Android解决程序切换后台被干掉,恢复状态问题
    Cookie
    dede后台搜索标签
  • 原文地址:https://www.cnblogs.com/vb4896/p/9489338.html
Copyright © 2011-2022 走看看