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;
}

浙公网安备 33010602011771号