【CF1286F】Harry The Potter(折半,子集卷积)

首先若用了第二种操作,就在 \((i,j)\) 之间连一条边。

发现图中不可能出现环,否则我们将这个环删掉,并用等同次数的一操作即可与环上原操作做到等价。

那么考虑一个数集 \(S=\{a_1,\cdots,a_k\}\) 有没有可能形成一条树而全部消掉。

考虑从下往上推,叶子 \(x\) 只有可能取它自己,而另一端有可能取 \(x\pm 1\)

从而倒数的某个点 \(y\) 第二层有可能取 \(y-\sum_{x\in son(y)}(x\pm 1)=y-\sum_{x\in son(y)}x+\{-|son(y)|,-|son(y)|+2,\cdots,|son(y)|-2,|son(y)|\}\)

最后我们要让根取 \(0\),这等价于 \(0\in \sum_{dep(x)\bmod 2=1}x-\sum_{dep(x)\bmod 2=0}x+\{-(k-1),-(k-1)+2,\cdots,(k-1)-2,k-1\}\)

最后得到等价条件是:

  • \(\sum a_i+(k-1)\) 为偶数。
  • 能将 \(S\) 划分为两个非空集合 \(A,B\),且 \(|\sum_{a\in A}a-\sum_{b\in B}b|\leq k-1\)

如果暴力地枚举 \(S,A\) 并检验是 \(O(3^n)\) 的。更好的做法是枚举 \(S\),然后折半,两半分别求出 \(2^{\frac{|S|}{2}}\) 种子集和并排序好(这里排序能边求边做),然后再双指针扫一下,时间复杂度 \(O(2^{\frac{|S|}{2}})\)。这部分总复杂度:

\[\sum_{i=1}^n\binom{n}{i}2^{i/2}=(1+\sqrt 2)^n \]

然后原问题相当于要选尽量多的 \(S_1,\cdots,S_m\) 不交且它们都能被表示出来,直接 DP 是 \(O(3^n)\) 的。更好的做法是二分 \(m\),然后转为 \(m\) 次幂的子集卷积,看是否有一位为 \(1\)。用倍增而非二分即可做到 \(O(n^2 2^n\log n)\)

卡常卡吐了。

#include<bits/stdc++.h>

#define ll long long

int main()
{
	int n;
	std::cin>>n;
	std::vector<ll> a;
	for(int i=0;i<n;i++)
	{
		ll x; std::cin>>x;
		if(x) a.push_back(x);
	}
	n=a.size();
	if(!n)
	{
		std::cout<<0;
		return 0;
	}
	int maxn=1<<n;
	std::vector<int> popc(maxn),f(maxn);
	for(int i=1;i<maxn;i++) popc[i]=popc[i>>1]+(i&1);
	for(int S=0;S<maxn;S++)
	{
		ll sum=0;
		std::vector<ll> b;
		for(int i=0;i<n;i++)
			if((S>>i)&1) b.push_back(a[i]),sum+=a[i];
		int nn=b.size();
		if(nn<=1||((sum+nn-1)&1)) continue;
		std::function<std::vector<ll>(int,int)> find
		=[&](int l,int r) -> std::vector<ll>
		{
			std::vector<ll> A{0},B;
			for(int k=l;k<=r;k++)
			{
				B=A;
				for(auto &x:A) x+=b[k];
				for(auto &x:B) x-=b[k];
				std::vector<ll> C; int i=0,j=0;
				while(i<(int)A.size()&&j<(int)B.size())
					C.push_back(A[i]<B[j]?A[i++]:B[j++]);
				while(i<(int)A.size()) C.push_back(A[i++]);
				while(j<(int)B.size()) C.push_back(B[j++]);
				A.swap(C);
			}
			return A;
		};
		int mid=nn/2;
		auto L=find(0,mid),R=find(mid+1,nn-1);
		ll lsum=0,rsum=0;
		for(int i=0;i<=mid;i++) lsum+=b[i];
		for(int i=mid+1;i<nn;i++) rsum+=b[i];
		bool f1=1,f2=1;
		for(int i=0,j=(int)R.size()-1;i<(int)L.size();i++)
		{
			while(j>=0&&L[i]+R[j]>nn-1) j--;
			if(j<0) break;
			if(f1&&L[i]==lsum)
			{
				if((R[j]!=rsum||j)&&L[i]+(R[j]==rsum?R[j-1]:R[j])>=-(nn-1)){f[S]=1;break;}
				f1=0;
			}
			else if(f2&&L[i]==-lsum)
			{
				if((R[j]!=-rsum||j)&&L[i]+(R[j]==-rsum?R[j-1]:R[j])>=-(nn-1)){f[S]=1;break;}
				f2=0;
			}
			else if(L[i]+R[j]>=-(nn-1)){f[S]=1;break;}
		}
	}
	auto fwt=[&](std::vector<int> &a) -> void
	{
		for(int bit=0,mid=1;mid<maxn;bit++,mid<<=1)
			for(int i=0,len=mid<<1;i<maxn;i+=len)
				for(int j=0;j<mid;j++) a[i+mid+j]-=a[i+j];
	};
	auto ifwt=[&](std::vector<int> &a) -> void
	{
		for(int bit=0,mid=1;mid<maxn;bit++,mid<<=1)
			for(int i=0,len=mid<<1;i<maxn;i+=len)
				for(int j=0;j<mid;j++) a[i+mid+j]-=a[i+j];
	};
	auto conv=[&](const std::vector<int> &A,const std::vector<int> &B) -> std::vector<int>
	{
		int sf=0,sg=0;
		std::vector<std::vector<int>> f,g,h;
		f=g=h=std::vector<std::vector<int>>(n+1,std::vector<int>(maxn));
		for(int i=0;i<maxn;i++)
			if(A[i]) f[popc[i]][i]=A[i],sf|=(1<<popc[i]);
		for(int i=0;i<maxn;i++)
			if(B[i]) g[popc[i]][i]=B[i],sg|=(1<<popc[i]);
		for(int i=0;i<=n;i++) if((sf>>i)&1) fwt(f[i]);
		for(int i=0;i<=n;i++) if((sg>>i)&1) fwt(g[i]);
		for(int i=0;i<=n;i++)
			if((sf>>i)&1) for(int j=0;i+j<=n;j++)
				if((sg>>j)&1) for(int k=0;k<maxn;k++)
					if(f[i][k]&&g[j][k]) h[i+j][k]+=f[i][k]*g[j][k];
		for(int i=2;i<=n;i++) ifwt(h[i]);
		std::vector<int> C(maxn);
		for(int i=0;i<maxn;i++) C[i]=h[popc[i]][i];
		return C;
	};
	std::vector<std::vector<int>> g(4);
	g[0]=f;
	for(int i=1;i<=3;i++)
		g[i]=conv(g[i-1],g[i-1]);
	int ans=0;
	std::vector<int> now(maxn);
	bool empty=1;
	for(int i=3;i>=0;i--)
	{
		if(empty)
		{
			bool flag=0;
			for(int j=0;j<maxn;j++)
				if(g[i][j]){flag=1;break;}
			if(flag) now=g[i],ans+=(1<<i),empty=0;
			continue;
		}
		auto tmp=conv(now,g[i]);
		bool flag=0;
		for(int j=0;j<maxn;j++)
			if(tmp[j]){flag=1;break;}
		if(flag) now=tmp,ans+=(1<<i);
	}
	std::cout<<n-ans;
	return 0;
}
posted @ 2022-10-31 10:12  ez_lcw  阅读(29)  评论(0编辑  收藏  举报