CF1856E2 PermuTree (hard version)
思路
对于一个子树来说,贡献是 \(\left(sum_{v\in son}[a_v<a_u]\right)\cdot\left(sum_{v\in son}[a_v>a_u]\right)\)。这个式子等价与将子树分为两部分,贡献就是这两部分大小的乘积。
很显然,这两个部分大小越接近 平分 越好,因为他们的和是一定的。
所以现在就转化为一个 \(01\) 背包问题,对每一个子树都做一遍。
但这是 \(O(n^2)\) 的做法,仅可以通过 E1。
由于和是一定的,那么由于大小大于 \(\sqrt n\) 的子树个数不超过 \(\sqrt n\) 个,所以子树大小种类的个数也是 \(\sqrt n\) 级别的。
于是可以转化为多重背包问题。
由于我们只注重能不能分出一种方案,于是我们可以使用 二进制优化 以及 bitset
,这样复杂度优化到 \(O\left(\frac{n\sqrt n}\omega\right)\)。
注意使用 bitset
时要实时根据大小开,否则空间开不下。这里使用模板元编程。
代码
#include <bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
#define int long long
#define fileio(x,y) freopen(x,"r",stdin),freopen(y,"w",stdout)
#define tup tuple<int,int,int>
#define pii pair<int,int>
#define bit(x) bitset<x>
#define pb emplace_back
#define mt make_tuple
#define mp make_pair
const int mod=998244353;
const int maxn=1e6+10;
vector<int> road[maxn],son[maxn],vec;
int n,siz[maxn];
int tar;
template<int size=1> int solve(int root,int fa)
{
int nowsize=siz[root]-1,lst=0,cnt=1;
if(nowsize+5>size) return solve<(min(2*size,1048576ll))>(root,fa);
sort(son[root].begin(),son[root].end()); vec.clear(); son[root].pb(-1);
for(int it:son[root])
{
if(!lst) { lst=it; continue; }
if(it!=lst)
{
int base=1;
while(cnt)
{
vec.pb(lst*min(cnt,base));
cnt-=min(cnt,base); base<<=1;
}
}
lst=it,cnt++;
}
bitset<size+5> dp; dp.set(size);
for(int it:vec) dp|=(dp>>it);
int pos=dp._Find_next(size-nowsize/2-1);
return (size-pos)*(nowsize-(size-pos));
}
void dfs(int root,int fa)
{
siz[root]=1;
for(int v:road[root])
{
if(v==fa) continue;
dfs(v,root); siz[root]+=siz[v],son[root].pb(siz[v]);
}
if(son[root].size()<=1) return;
if(son[root].size()==2) return (void)(tar+=son[root][0]*son[root][1]);
for(int it:son[root])
if(it>=(siz[root]-1)/2)
return (void)(tar+=it*(siz[root]-1-it));
tar+=solve<8>(root,fa);
return;
}
void work()
{
/* Code */
cin>>n;
for(int i=1,x,y; i<n; i++) cin>>x,y=i+1,road[x].pb(y),road[y].pb(x);
// for(int i=1,x,y; i<n; i++) cin>>x>>y,road[x].pb(y),road[y].pb(x);
dfs(1,0); cout<<tar<<'\n';
return;
}
signed main()
{
// fileio("tree.in","tree.out");
ios::sync_with_stdio(false);
cin.tie(0);
int t,stTime=clock();
t=1;
while(t--)
work();
// cerr<<"Time : "<<(double)(clock()-stTime)/CLOCKS_PER_SEC<<'\n';
return 0;
} // Texas yyds !!!