首先若用了第二种操作,就在 ( 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}。
最后得到等价条件是:
- ∑ a i + ( k − 1 ) \sum a_i+(k-1) ∑ai+(k−1) 为偶数。
- 能将 S S S 划分为两个非空集合 A , B A,B A,B,且 ∣ ∑ a ∈ A a − ∑ b ∈ B b ∣ ≤ k − 1 |\sum_{a\in A}a-\sum_{b\in B}b|\leq k-1 ∣∑a∈Aa−∑b∈Bb∣≤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<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;
}