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

题目大意:

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

 

题解:

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

进一步挖掘一下性质:

s=log2n+1,t4

ks+t2log2n+1+42log2n+3

可以得到ak2k3,根据Brauer chain的性质,必须有ai1>=ai2. 从ak倒着推回去可以推出任意0ik, ai2i3.

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

然后想到没必要让ks+t2取等号,因为等号的情况可以直接构造。所以我们让爆搜的条件更加严格一点,k<s+t2 也就是说ks+t3. 重新顺着刚才的思路推导一遍得到更强的条件ai2i2。 然后爆搜就只要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\n", k = ans[n].size() - 1);
64         
65         for (int i = k; i >= 1; --i)
66         {
67             printf("%d %d %d\n", ans[n][i], ans[n][i - 1], ans[n][i] - ans[n][i - 1]);
68         }
69     }
70     
71     
72     return 0;
73 }
复制代码

 

posted @   lzw4896s  阅读(526)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
历史上的今天:
2014-08-16 常州培训 day6 解题报告
点击右上角即可分享
微信分享提示