P12025 [USACO25OPEN] Sequence Construction S

题目传送门

这里是我的博客喵

推荐一下这篇题解,写的很清晰,赞一个。


一眼构造题。

我们如果优先考虑 2 条件的话,那 3 条件就不好满足了。而有异或的题,经典做法就是拆位。于是乎我们考虑 3 条件,把 \(k\) 进行一个拆位。

这里我们要想办法让构造出来的 \(a\) 数组加和尽可能小,因为 \(a\) 数组小了可以想办法通过适当的构造,使得不改变 \(popcount\) 异或值的情况下满足条件 2。但是如果 \(a\) 数组大了,就不满足条件 2 了。

那我们怎样让 \(a\) 数组加和最小呢?假设当前 \(k\) 上第 \(p\) 位为 1,那么第一,我们只让 \(a\) 数组中的一个数的 \(popcount\) 在这一位为 1。(如果有多个在这一位上为 1 的 \(popcount\),那显然 \(a\) 数组加和会更大)

同理,如果第 \(p\) 位为 0,那我们就要尽可能地不让多个 \(popcount\) 这一位上为 1。

第二我们拆位,把 \(k\) 所有的 1 单独拆出来,比如 101 拆成 100 和 1。

假设当前第 \(p\) 位上是 1 的话(\(p \in [0,4]\)),那说明有一个 \(popcount\)\(2^p\)。那它对应的数最小为 \(2^{2^p}-1\)

例如 \(k=5\) 时,\(5_{10}\) 又能表示为 \((101)_2\),这样的话,最少 \(a\) 数组里面也得有 \((1111)_2\)\((1)_2\) 两个数。

为什么我们要把 \(k\) 的 1 都单独拆成一个一个的呢?还是考虑 \(k=5\) 的情况,如果拆成 101 和 0 的话,显然 \(2^{2^2+2^0}-1 + 0 > 2^{2^2}-1 + 2^{2^0}-1\)


构造出来这样的 a 数组后,我们还可能需要经过调整满足条件 2。由于还要考虑无解的情况,所以我们分讨一下。

(以下记 \(sum=\sum\limits_{i=1}^{n}{a_i}\),其中 \(n\) 是调整前 \(a\) 数组的数字个数)


  1. \(sum>m\)

无解。因为我们构造的 \(a\) 数组是在满足条件 3 的情况下加和最小的。\(m\) 比这个东西还小的话,我们就没办法了。


  1. \(sum==m\)

直接输出 \(a\) 数组,不需要进行任何其他处理。


  1. \(m-sum==1\)

(1). \(a\) 数组中含有 1

由于 \(popcount(1)==popcount(2)\),所以我们把原数组里的 1 换成 2,就能满足条件。

(2). \(a\) 数组里没有 1

无解。因为我们 \(a\) 数组中的数都是形如 \(2^p-1\) 的(而且没有 1)。一方面 \(popcount\) 异或和的第 0 位为 0,所以不能加入 1;另一方面,除 1 以外的某个 \(2^p-1\) 加上 1 变成 \(2^p\) 时,\(popcount\) 就改变了,\(popcount\) 的异或和就变了。


  1. \(m-sum\) 为偶数

我们设 \(x=(m-sum)/2\),这样我们往 \(a\) 数组里加入两个 \(x\),一方面加和满足条件 2,另一方面 \(popcount(x) \oplus popcount(x)=0\),所以也满足条件 3。


  1. \(m-sum\) 为奇数且 \(3 \le m-sum\)

\(a\) 数组里加入两个数 1,2,由于 \(popcount(1)==popcount(2)\),所以 \(sum\) 加上 3 且 \(popcount\) 的异或和不变。

\(m-sum\) 可就变成偶数了。我们仿照情况 4,加入两个 \(x\) 即可。


综上,这就是我们的构造方案。由于 \(k \le 31\),所以 \(k\) 最多 5 个二进制位,所以 \(a\) 的元素个数最多为 \(5 + 4 = 9 < 100\),一定满足限制 1。

代码:

P12025
#include<cstdio>
#include<iostream>
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000096,stdin),p1==p2)?EOF:*p1++)
using namespace std;

char *p1,*p2,buf[1000100];
inline int read(){
	int x=0,f=1;char c=gc();
	while(c<48){
		if(c=='-') f=-1;
		c=gc();
	}
	while(c>47) x=(x<<1)+(x<<3)+(c^48),c=gc();
	return x*f;
}

const int N=120;
int T,m,k,a[N],awa;
//awa:同题解中的n 

signed main(){
	T=read();
	while(T--){
		m=read(),k=read();
		int sum=0,cnt=0;
		int qwq=k;
		while(qwq){
			int t=qwq&1;
			if(t){
				a[++awa]=(1<<((1<<cnt)))-1;
			}
			qwq>>=1;
			cnt++;
		}
		for(register int i=1;i<=awa;i++){
			sum+=a[i];
		}
		if(sum>m){
			//case1,无解 
			printf("-1\n");
		}
		else if(sum==m){
			//case2,直接输出 
			printf("%d\n",awa);
			for(register int i=1;i<=awa;i++){
				printf("%d ",a[i]);
			}
			printf("\n");
		}
		else{
			if(m-sum==1){
				if(a[1]==1){
					//case3.1,把1换成2 
					printf("%d\n2",awa);
					for(register int i=2;i<=awa;i++){
						printf(" %d",a[i]);
					}
					printf("\n");
				}
				else{
					//case3.2,无解 
					printf("-1\n");
				}
			}
			else if((m-sum)&1){
				//case 5:加入1,2,x,x 
				a[++awa]=1,a[++awa]=2;sum+=3;
				int x=(m-sum)>>1;
				a[++awa]=x;a[++awa]=x;
				printf("%d\n",awa);
				for(register int i=1;i<=awa;i++){
					printf("%d ",a[i]);
				}
				printf("\n");
			}
			else{
				//case 4:加入x,x 
				int x=(m-sum)>>1;
				a[++awa]=x;a[++awa]=x;
				printf("%d\n",awa);
				for(register int i=1;i<=awa;i++){
					printf("%d ",a[i]);
				}
				printf("\n");
			}
		}
		//多测记得初始化 
		for(register int i=1;i<=awa;i++){
			a[i]=0;
		}
		awa=0;
	}
	return 0;
}
posted @ 2025-11-07 09:48  qwqSW  阅读(3)  评论(0)    收藏  举报