首先若用了第二种操作,就在 ( i , j ) (i,j) (i,j) 之间连一条边。
发现图中不可能出现环,否则我们将这个环删掉,并用等同次数的一操作即可与环上原操作做到等价。
那么考虑一个数集 S = { a 1 , ⋯ , a k } S=\{a_1,\cdots,a_k\} S={a1,⋯,ak} 有没有可能形成一条树而全部消掉。
考虑从下往上推,叶子 x x x 只有可能取它自己,而另一端有可能取 x ± 1 x\pm 1 x±1。
从而倒数的某个点 y y y 第二层有可能取 y − ∑ x ∈ s o n ( y ) ( x ± 1 ) = y − ∑ x ∈ s o n ( y ) x + { − ∣ s o n ( y ) ∣ , − ∣ s o n ( y ) ∣ + 2 , ⋯ , ∣ s o n ( y ) ∣ − 2 , ∣ s o n ( 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)|\} y−∑x∈son(y)(x±1)=y−∑x∈son(y)x+{−∣son(y)∣,−∣son(y)∣+2,⋯,∣son(y)∣−2,∣son(y)∣}。
最后我们要让根取 0 0 0,这等价于 0 ∈ ∑ d e p ( x ) m o d 2 = 1 x − ∑ d e p ( x ) m o d 2 = 0 x + { − ( k − 1 ) , − ( k − 1 ) + 2 , ⋯ , ( k − 1 ) − 2 , k − 1 } 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\} 0∈∑dep(x)mod2=1x−∑dep(x)mod2=0x+{−(k−1),−(k−1)+2,⋯,(k−1)−2,k−1}。
最后得到等价条件是:
如果暴力地枚举 S , A S,A S,A 并检验是 O ( 3 n ) O(3^n) O(3n) 的。更好的做法是枚举 S S S,然后折半,两半分别求出 2 ∣ S ∣ 2 2^{\frac{|S|}{2}} 22∣S∣ 种子集和并排序好(这里排序能边求边做),然后再双指针扫一下,时间复杂度 O ( 2 ∣ S ∣ 2 ) O(2^{\frac{|S|}{2}}) O(22∣S∣)。这部分总复杂度:
∑ i = 1 n ( n i ) 2 i / 2 = ( 1 + 2 ) n \sum_{i=1}^n\binom{n}{i}2^{i/2}=(1+\sqrt 2)^n i=1∑n(in)2i/2=(1+2)n
然后原问题相当于要选尽量多的 S 1 , ⋯ , S m S_1,\cdots,S_m S1,⋯,Sm 不交且它们都能被表示出来,直接 DP 是 O ( 3 n ) O(3^n) O(3n) 的。更好的做法是二分 m m m,然后转为 m m m 次幂的子集卷积,看是否有一位为 1 1 1。用倍增而非二分即可做到 O ( n 2 2 n log n ) O(n^22^n\log n) O(n22nlogn)。
卡常卡吐了。
#include
#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;
}