1 条题解

  • 0
    @ 2025-8-24 23:04:54

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Grand_Dawn
    小WA撑小艇,偷采AC回

    搬运于2025-08-24 23:04:54,当前版本为作者最后更新于2024-04-03 16:06:15,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    mex1 题解

    checker

    解决该问题,知道如何写 checker 是必要的。

    首先,如果 ai>na_i>n,则可以直接令 aina_i\leftarrow n,因为一个子集最多有 nn 个元素,mex\text{mex} 最大为 n1n-1

    又可以发现相同的数字之间没有区别,所以可以设 calc(n,cnt0,cnt1,,cntn)\text{calc}(n,cnt_0,cnt_1,\cdots,cnt_n) 表示所有子集 mex\text{mex} 求和。

    如果不选取 00,该部分答案显然为 00

    如果选取了至少一个 00,那么共有 2cnt012^{cnt0}-1 种选取 00 的方案。

    而对于选取非 00 的元素,ans=calc(ncnt0,cnt1,,cntn)ans=\text{calc}(n-cnt_0,cnt_1,\cdots,cnt_n) 已经计算出所有这些元素 1-1 的子集 mex\text{mex} 求和。

    而且又有这些子集共有 2ncnt02^{n-cnt_0} 个。因此,答案为 (ans+2ncnt0)(2cnt01)(ans+2^{n-cnt_0})(2^{cnt_0}-1)

    因此可以在 Θ(n)\Theta(n) 的时间复杂度内算出答案。

    性质

    显然以上的过程是唯一确定的,可以认为是一种递推过程。

    即可以定义 f(x,n)f(x,n) 表示用 nn 个元素凑成答案为 xx 是否存在解;如果存在解,则记录 cnt0cnt_0 可能的值。直接枚举 cnt0cnt_0 是否存在解。

    递推式:$f(x,n)\overset{\text{or}}\leftarrow f(\frac{x}{2^i-1}-2^{n-i},n-i)$。

    边界条件:

    1. f(0,n)=truef(0,n)=\textbf{true},此时需要填入 nn11 符合答案。

    2. x>0x>0f(x,0)=falsef(x,0)=\textbf{false}

    3. x<0x<0f(x,n)=falsef(x,n)=\textbf{false}

    如果采用递推可以获得 5555 分。采取枚举 nn 后记忆化搜索可以获得 6060 分。

    优化下界

    可以发现很多记忆化数组中很多的答案都是 false\textbf{false},因此可以对于固定的 nn 可以找到 xx 的上下界使得 f(x,n)falsef(x,n)\neq \textbf{false}

    可以发现,当 x<2nx<2^n 时,f(x,n)falsef(x,n)\neq\textbf{false} 当且仅当 x=2n2kx=2^n-2^kkk 为整数且 0kn0\le k\le n),以下进行证明:

    初始时先令 ai=na_i=n,考虑从小到大填入真正的 aka_k 的过程,加入 aka_k 后,mex=c(cZ)\text{mex}=c(c\in \mathbb{Z}) 的数量均为 2nk2^{n-k} 的倍数,在于剩下的 nkn-knn 可以任意选择选与不选。

    若加入 aka_kxx 发生变化,则由以上性质得至少增加 2nk2^{n-k}

    若加入 aka_kxx 不变化,则 aka_k 数组中不存在 ak1a_k-1,因此等价于之后均不会增加 xx

    因此若加入 aka_k 之前每个元素均发生变化,则 xx 至少为 i=1k2ni=2n2k\sum\limits_{i=1}^{k}2^{n-i}=2^n-2^k。又由于是 2nk2^{n-k} 得倍数,且 x<2nx<2^n,故 x=2n2kx=2^n-2^k

    因此证明完毕,构造仅需采用 kk00nkn-knn 即可。

    实际上,下界仅需要设定为 2n12^{n-1} 就可以通过此题。

    优化上界

    可以发现有以下性质:如果 xx 取到上界,那么 cnt0cnt1cnt2cntncnt_0\ge cnt_1\ge cnt_2\ge\cdots\ge cnt_n

    证明:如果 cntk<cntk+1cnt_k<cnt_{k+1},那么对比交换 cntkcnt_kcntk+1cnt_{k+1} 的结果:

    那么对于 c>k+1c>k+1mex=c\text{mex}=c 的子集数量为 i=1c1(2i1)\prod\limits_{i=1}^{c-1}(2^i-1),数量不变。

    对于 ckc\le kmex=c\text{mex}=c 的子集数量为 i=1c1(2i1)\prod\limits_{i=1}^{c-1}(2^i-1),数量不变。

    mex=k+1\text{mex}=k+1 的子集数量 i=1k(2i1)\prod\limits_{i=1}^{k}(2^i-1),数量变多。

    因此答案会变大。使用冒泡排序可证明以上性质。

    因此使用拆分数暴力枚举可以确定上确界。

    实际上,上界仅需要设定为 n2n1n\cdot 2^{n-1}(所有子集长度之和)就可以通过此题。上界设定为 n2nn\cdot 2^n 应该不能通过。

    #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
    上传者