1 条题解
-
0
自动搬运
来自洛谷,原作者为

2020HZ06
**搬运于
2025-08-24 22:25:57,当前版本为作者最后更新于2024-10-29 21:08:12,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
本篇题解参考 https://www.cnblogs.com/vb4896/p/9489338.html 在此致谢。
注意拆分后得到的数会去重。因此有一个方案是假设 有 位而其中有 位 。那么可以每次拆出 并让 。拆成若干 之后从最高位不停 并去重最终即可达到 。代价 。这是答案的上界。
这个过程可以视作加法链,,求最短加法链长度 。
然而有时候不是这样。, 加法链长度只有 。而 。
现在有两条性质:
- ,显然。不然 拼不出来。
- 。因为 。得到 ,又因为 ,两边同时作为指数,得到 ,由性质一得 ,,。
考虑 可以直接构造,只需搜索 的情况即可。类似的有 。直接搜即可。
接下来有几种搜索方法:
方法一:每组数据搜索,枚举 位置填什么,最优性和可行性剪枝。
很遗憾,
1 13 17 19 20直接能把它卡 T 飞。
方法二:枚举值域太傻,枚举当前链中元素暴力组合。由于元素是 量级,单组数据能很快出解。
然而 会导致 TLE on test 2。
方法三:只进行一次 DFS 预处理出所有数的答案。注意 的 的个数不超过 。另外还可以发现一条性质:
每次往链中加元素时 一定成立。因为 如果拼出来却暂时不用完全可以到后面用到再拼。
举个例子,假设现在链中元素 。要拼出 ,一种方案是 。但是我们也可以 。也就是说,, 完全可以把 的累加贡献后移变成 ,次数不变。
因此直接枚举 扩展即可。只需一重循环。
#include<bits/stdc++.h> using namespace std; #define ll long long #define pb push_back int T,d1,d2,d3,d4,n,s,t; int a[35]; vector<int>ans[4*(1<<20)+5],v; void dfs(int nw){ if(nw>=25||(nw>=2&&a[nw]<(1<<nw-2))) return;//24=max(s+t-3)=23+4-3 if(__builtin_popcount(a[nw])<=4&&(!ans[a[nw]].size()||ans[a[nw]].size()>nw)){//更新答案 ans[a[nw]].clear(); for(int i=0;i<=nw;i++) ans[a[nw]].pb(a[i]); } for(int i=0;i<=nw;i++){//扩展 a[nw+1]=a[nw]+a[i]; if(a[nw+1]<=(1<<22)) dfs(nw+1); else break; } } int main(){ scanf("%d",&T); a[0]=1; dfs(0); for(int i=1;i<=(1<<22);i++){ if(__builtin_popcount(i)<=4&&!ans[i].size()){//答案为 s+t-2 for(int j=0;(1<<j)<=i;j++) ans[i].pb(1<<j); v.clear(); int u=i; while(u!=(u&-u)) v.pb(u),u-=(u&-u); for(int j=v.size()-1;j>=0;j--) ans[i].pb(v[j]);//倒着插回去 } } while(T--){ scanf("%d%d%d%d",&d1,&d2,&d3,&d4); n=(1<<d1)+(1<<d2)+(1<<d3)+(1<<d4); printf("%d\n",ans[n].size()-1); for(int i=ans[n].size()-1;i>0;i--) printf("%d %d %d\n",ans[n][i],ans[n][i-1],ans[n][i]-ans[n][i-1]);//直接相减构造答案 } return 0; }
- 1
信息
- ID
- 6182
- 时间
- 3000ms
- 内存
- 512MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者