题解:P16309 [ICPC 2023 Jinan R] 图划分 2
lailai0916 · · 题解
题意简述
删除树上的若干条边。
要求每个剩余连通块的大小都是
求不同删边方案的数量。
解题思路
将树根定为
记
这个连通块还可能通过父边继续扩大。
因此,只需保留
设当前状态为
若保留边
若删除边
直接对每个节点执行稀疏背包已经可以通过。 但大量单儿子链仍会重复搬运相同状态。 下面进一步压缩这些转移。
若儿子
这类节点无需建立动态规划数组。 它们只通过子树大小参与最近保留祖先的强制重量。
只保留子树大小至少为
这些小子树以后无需单独参与背包。
若某个
所有强制重量覆盖的原树节点互不重复。 所以:
若
若
同时,切断儿子边会产生额外状态:
使用 deque 保存整段状态。
向前补
由于所有
若
代码先迭代求出父亲、遍历顺序与子树大小。 再按逆序计算动态规划,避免递归栈溢出。
最后,根节点的连通块也必须封闭。 答案为:
下面说明分叉处背包的总代价。
一个状态的可能大小数量至多为:
设一次合并两侧的子树规模为
若两侧都至少为
若一次合并使累计规模首次达到
其余合并都位于规模小于
另外,分叉节点的儿子数量之和为
因此,时间复杂度与空间复杂度都是
参考代码
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
using pii=pair<int,int>;
const int N=100005;
const int mod=998244353;
vector<int> G[N],buf;
vector<pii> x,y;
deque<int> f[N];
int fa[N],siz[N],ord[N];
void merge(deque<int> &a,deque<int> &b,int k)
{
x.clear();
y.clear();
for(int i=1;i<=k+1;i++)
{
if(a[i])x.push_back({i,a[i]});
if(b[i])y.push_back({i,b[i]});
}
fill(buf.begin(),buf.end(),0);
int cut=(b[k]+b[k+1])%mod;
for(auto [i,p]:x)
{
buf[i]=(buf[i]+(ll)p*cut)%mod;
for(auto [j,q]:y)
{
if(i+j>k+1)break;
buf[i+j]=(buf[i+j]+(ll)p*q)%mod;
}
}
a.assign(buf.begin(),buf.end());
deque<int>().swap(b);
}
void solve()
{
int n,k;
cin>>n>>k;
for(int i=1;i<n;i++)
{
int u,v;
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
int cnt=1;
ord[1]=1;
fa[1]=0;
for(int i=1;i<=cnt;i++)
{
int u=ord[i];
for(int v:G[u])
{
if(v==fa[u])continue;
fa[v]=u;
ord[++cnt]=v;
}
}
for(int i=n;i;i--)
{
int u=ord[i];
siz[u]++;
if(fa[u])siz[fa[u]]+=siz[u];
}
buf.resize(k+2);
bool ok=1;
for(int i=n;i&&ok;i--)
{
int u=ord[i],s=1,c=0,v=0;
if(siz[u]<k)continue;
for(int j:G[u])if(fa[j]==u)
{
if(siz[j]<k)s+=siz[j];
else{c++;v=j;}
}
if(s>k+1)
{
ok=0;
break;
}
if(!c)
{
f[u].assign(k+2,0);
f[u][s]=1;
}
else if(c==1)
{
int z=(f[v][k]+f[v][k+1])%mod;
f[u]=move(f[v]);
for(int j=0;j<s;j++)
{
f[u].push_front(0);
f[u].pop_back();
}
f[u][s]=(f[u][s]+z)%mod;
}
else
{
f[u].assign(k+2,0);
f[u][s]=1;
for(int j:G[u])if(fa[j]==u&&siz[j]>=k)merge(f[u],f[j],k);
}
}
int ans=ok?(f[1][k]+f[1][k+1])%mod:0;
cout<<ans<<'\n';
for(int i=1;i<=n;i++)
{
G[i].clear();
deque<int>().swap(f[i]);
siz[i]=0;
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin>>t;
while(t--)solve();
return 0;
}