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

Grand_Dawn
小WA撑小艇,偷采AC回搬运于
2025-08-24 23:04:54,当前版本为作者最后更新于2024-04-03 16:06:15,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
mex1 题解
checker
解决该问题,知道如何写 checker 是必要的。
首先,如果 ,则可以直接令 ,因为一个子集最多有 个元素, 最大为 。
又可以发现相同的数字之间没有区别,所以可以设 表示所有子集 求和。
如果不选取 ,该部分答案显然为 。
如果选取了至少一个 ,那么共有 种选取 的方案。
而对于选取非 的元素, 已经计算出所有这些元素 的子集 求和。
而且又有这些子集共有 个。因此,答案为 。
因此可以在 的时间复杂度内算出答案。
性质
显然以上的过程是唯一确定的,可以认为是一种递推过程。
即可以定义 表示用 个元素凑成答案为 是否存在解;如果存在解,则记录 可能的值。直接枚举 是否存在解。
递推式:$f(x,n)\overset{\text{or}}\leftarrow f(\frac{x}{2^i-1}-2^{n-i},n-i)$。
边界条件:
-
,此时需要填入 个 符合答案。
-
时 。
-
时 。
如果采用递推可以获得 分。采取枚举 后记忆化搜索可以获得 分。
优化下界
可以发现很多记忆化数组中很多的答案都是 ,因此可以对于固定的 可以找到 的上下界使得 。
可以发现,当 时, 当且仅当 ( 为整数且 ),以下进行证明:
初始时先令 ,考虑从小到大填入真正的 的过程,加入 后, 的数量均为 的倍数,在于剩下的 个 可以任意选择选与不选。
若加入 时 发生变化,则由以上性质得至少增加 。
若加入 时 不变化,则 数组中不存在 ,因此等价于之后均不会增加 。
因此若加入 之前每个元素均发生变化,则 至少为 。又由于是 得倍数,且 ,故 。
因此证明完毕,构造仅需采用 个 和 个 即可。
实际上,下界仅需要设定为 就可以通过此题。
优化上界
可以发现有以下性质:如果 取到上界,那么 。
证明:如果 ,那么对比交换 和 的结果:
那么对于 的 的子集数量为 ,数量不变。
对于 的 的子集数量为 ,数量不变。
而 的子集数量 ,数量变多。
因此答案会变大。使用冒泡排序可证明以上性质。
因此使用拆分数暴力枚举可以确定上确界。
实际上,上界仅需要设定为 (所有子集长度之和)就可以通过此题。上界设定为 应该不能通过。
#include<bits/stdc++.h> #define N 35 #define INF 2000000009 using namespace std; void write(int x){ if(x>9)write(x/10); putchar(x%10+'0'); } int pw[N],mx[N]; template<class first,class second,int C> struct Unordered_map{ struct node{ first t; second w; int nxt; }e[C+9]; int tot,hd[C+9]; int hash(first x){ return x%C; } second& operator [](first a){ int x=hash(a); for(int i=hd[x];i;i=e[i].nxt){ if(e[i].t==a)return e[i].w; } e[++tot].t=a; e[tot].nxt=hd[x]; hd[x]=tot; return e[tot].w; } second find(first a){ int x=hash(a); for(int i=hd[x];i;i=e[i].nxt){ if(e[i].t==a)return e[i].w; } return -1; } }; Unordered_map<int,char,300009>mp[N]; int check(int x,int n){ if(x==0)return 1; if(x>mx[n])return 0; if(mp[n].find(x)!=-1)return mp[n][x]; if(x<pw[n])return 0; for(int i=1;i<=n;i++){ if(x%(pw[i]-1))continue; if(check(x/(pw[i]-1)-pw[n-i],n-i))return mp[n][x]=i; } return mp[n][x]=0; } void solve(){ int x;cin>>x; for(int n=1;pw[n-1]<=x+1;n++) if(check(x,n)){ int id=0; write(n);putchar('\n'); while(x){ int pos=mp[n][x]; for(int i=pos;i>=1;i--)write(id),putchar(' '); x=x/(pw[pos]-1)-pw[n-pos]; n-=pos;id++; } for(int i=n;i>=1;i--)write(id+1),putchar(' '); putchar('\n'); return; } puts("-1"); } int b[39]; int calc(){ int c=0;while(b[c])c++; int ans=0,cnt=0; while(c>0){ ans=(ans+pw[cnt])*(pw[b[--c]]-1); cnt+=b[c]; } return ans; } int dfs(int x,int lft,int lst=1000000000){ if(lft==0)return calc(); int ans=0; for(int i=min(lft,lst);i>=1;i--){ b[x]=i; ans=max(ans,dfs(x+1,lft-i,i)); b[x]=0; } return ans; } int main(){ //freopen("10-1.in","r",stdin); //freopen("a.out","w",stdout); pw[0]=1;for(int i=1;i<=31;i++)pw[i]=pw[i-1]*2;pw[32]=INF; for(int i=1;i<=31;i++){ if(i<=28)mx[i]=dfs(0,i); else mx[i]=INF; } for(int i=1;i<=32;i++) for(int j=i-1,tmp=0;j>=0&&tmp<=INF;j--){ tmp+=pw[j]; mp[i][tmp]=i-j; } int t;cin>>t; while(t--)solve(); return 0; } -
- 1
信息
- ID
- 10003
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者