CF1286F

可能更好的阅读体验

全部用一操作即可做到 \(n\) 次操作。

考虑什么时候操作次数可以减少,发现二操作形成的树形结构可以减少一次操作。

\(g_S\) 为“集合 \(S\) 中的点能否用二操作构成的一个树形结构解决”,容易 DP 求出最多的树形结构个数,用 \(n\) 减去这个数即为答案。

考虑怎么求 \(g_S\),首先 \(a_i=0\) 也可以看作是树形结构。

考虑将树黑白染色,也就是将 \(S\) 划分为两个非空集合 \(i,j\)。若不考虑多的 \(1\) 那么 \(sum_i=sum_j\) 即可。

考虑给每条边多的 \(1\) 定向,能够得到的差即为 \([-\lvert S\rvert+1,\lvert S\rvert-1]\) 范围内的偶数。

设集合 \(S\) 中元素的和为 \(sum_S\),那么只要 \(sum_i-sum_j\in[-\lvert S\rvert+1,\lvert S\rvert-1]\)\(sum_S\)\(\lvert S\rvert-1\) 奇偶性相同即可。

时间复杂度 \(O(3^n)\),注意到时限为 9 秒,能过。

#include<bits/stdc++.h>
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define per(i,r,l) for(int i=(r);i>=(l);i--)
#define repll(i,l,r) for(ll i=(l);i<=(r);i++)
#define perll(i,r,l) for(ll i=(r);i>=(l);i--)
#define pb push_back
#define ins insert
#define clr clear
using namespace std;
namespace ax_by_c{
typedef long long ll;
const int N=25;
const int S=(1<<20)+5;
int lb(int x){
	return x&(-x);
}
int n,msk,lg2[S],pct[S],f[S];
ll a[N],sum[S];
bool g[S];
void slv(){
	scanf("%d",&n);
	rep(i,1,n){
		scanf("%lld",&a[i]);
		if(!a[i])g[1<<(i-1)]=1;
	}
	msk=(1<<n)-1;
	rep(i,1,n)lg2[1<<(i-1)]=i;
	rep(i,0,msk)pct[i]=pct[i>>1]+(i&1);
	rep(i,1,msk)sum[i]=sum[i^lb(i)]+a[lg2[lb(i)]];
	rep(i,1,msk)if(!(sum[i]%2)==!((pct[i]-1)%2))for(int j=(i-1)&i;j;j=(j-1)&i)if(abs(sum[j]-sum[i^j])<=pct[i]-1){
		g[i]=1;
		break;
	}
	rep(i,1,msk){
		if(g[i])f[i]=max(f[i],1);
		for(int j=(i-1)&i;j;j=(j-1)&i)f[i]=max(f[i],f[j]+f[i^j]);
	}
	printf("%d\n",n-f[msk]);
}
void main(){
	int T=1;
//	int csid=0;scanf("%d",&csid);
//	scanf("%d",&T);
	while(T--)slv();
}
}
int main(){
	string __name="";
	if(__name!=""){
		freopen((__name+".in").c_str(),"r",stdin);
		freopen((__name+".out").c_str(),"w",stdout);
	}
	ax_by_c::main();
	return 0;
}
posted @ 2025-04-22 13:33  ax_by_c  阅读(8)  评论(0)    收藏  举报