折半搜索
meet in the middle
对于方案数问题的搜索,以选或不选为例,相当于在一定限制的条件下做枚举,所以最坏的复杂度是
做一个图:
我们可以把 N 分成两块,分别做搜索再把方案合并起来
如果能做到高效地合并方案,搜索的复杂度就成功地降下去了,变成
我们发现,这显然得到了两个子问题啊!那我们是不是能再分下去呢!
然而还要考虑合并方案带来的复杂度,由于方案数即为搜索规模量,即使搜索能再降,合并方案的问题仍然卡在那...
如果能够快速合并方案数,那么恭喜你,你很可能在写一棵线段树之类的...至少我开心地写着写着发现...
void mergeSearch(int x, int l, int r)
{
if (l == r) { if (A[l] <= M) Q[x][++tot[x]] = A[l]; } // Q[x][0] = 0;
else {
int mid = (l + r) >> 1;
mergeSearch(lson, l, mid);
mergeSearch(rson, mid+1, r);
sort(Q[lson], Q[lson]+tot[lson]);
sort(Q[rson], Q[rson]+tot[rson]);
for (int i=0; i<tot[lson]; i++) {
for (int j=0; j<tot[rson]; j++) {
// ???
}
}
}
}
这特么不是线段树吗??!
但合并的问题卡在那,失败。
至少对于带幂的复杂度来说,幂能减半真的漂亮!
模板题:P4799
void dfs(int l, int r, ll sum, int tp)
{
if (l > r) Q[tp][++tot[tp]] = sum;
else {
if (sum + A[l] <= M) dfs(l+1, r, sum+A[l], tp);
dfs(l+1, r, sum, tp);
}
}
dfs(1, N>>1, 0, 0);
dfs((N>>1)+1, N, 0, 1);
// 然后是合并的操作
// ...
天平问题:把物品分成两部分,用两座天平秤(状态/权值变成 -1, 0, 1),把两座天平各自称过的所有重量拿去合并