题解 P1623 【[CEOI2007]树的匹配Treasury】
xtx1092515503 · · 题解
个人觉得树上背包的做法更为易懂,并且只需要高精度加法和乘法。
我们令
注意,当父亲和儿子均没有匹配时(即
这里先贴出使用long long的
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,g[1010][2];
ll f[1010][510][2],h[1010][2];
//f[i,j,0/1]:the number of situations when there're j matchings in subtree i and i itself isn't/is matched; and g is the maximal matchings. h is an array for assistance.
vector<int>v[1010];
bool occ[1010];
void dfs(int x){
f[x][0][0]=1;
for(auto y:v[x]){
dfs(y);
int tmp0=g[x][0]+max(g[y][0],g[y][1]),tmp1=max(g[x][1]+max(g[y][0],g[y][1]),g[x][0]+g[y][0]+1);
for(int i=0;i<=tmp0;i++)h[i][0]=0;
for(int i=0;i<=tmp1;i++)h[i][1]=0;
for(int i=0;i<=g[x][0];i++)for(int j=0;j<=g[y][0];j++)h[i+j][0]+=f[x][i][0]*f[y][j][0],h[i+j+1][1]+=f[x][i][0]*f[y][j][0];
for(int i=0;i<=g[x][1];i++)for(int j=0;j<=g[y][0];j++)h[i+j][1]+=f[x][i][1]*f[y][j][0];
for(int i=0;i<=g[x][0];i++)for(int j=0;j<=g[y][1];j++)h[i+j][0]+=f[x][i][0]*f[y][j][1];
for(int i=0;i<=g[x][1];i++)for(int j=0;j<=g[y][1];j++)h[i+j][1]+=f[x][i][1]*f[y][j][1];
for(int i=0;i<=tmp0;i++)f[x][i][0]=h[i][0];
for(int i=0;i<=tmp1;i++)f[x][i][1]=h[i][1];
g[x][0]=tmp0;
g[x][1]=tmp1;
}
}
int main(){
scanf("%d",&n);
for(int i=1,x,y,z;i<=n;i++){
scanf("%d%d",&x,&y);
while(y--)scanf("%d",&z),v[x].push_back(z),occ[z]=true;
}
for(int i=1;i<=n;i++){
if(occ[i])continue;
dfs(i);
int mx=max(g[i][0],g[i][1]);
ll ret=0;
if(mx==g[i][0])ret+=f[i][g[i][0]][0];
if(mx==g[i][1])ret+=f[i][g[i][1]][1];
printf("%d\n%lld\n",mx,ret);
}
return 0;
}
剩下的部分需要高精度。但明显,上述算法本身复杂度已经达到
那怎么办呢?
观察到我们只需要求最大的方案数。这意味着我们只需要记录最大值附近的几个DP值即可——剩余的DP值无论怎么搞都不可能最终成为最大值。于是这样搞正常DP的复杂度就被优化到了
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,g[1010][2];
struct Wint:vector<int>{
Wint(){clear();}
Wint(int x){clear();while(x)push_back(x%10),x/=10;}
void operator ~(){
for(int i=0;i+1<size();i++)(*this)[i+1]+=(*this)[i]/10,(*this)[i]%=10;
while(!empty()&&back()>9){
int tmp=back()/10;
back()%=10;
push_back(tmp);
}
while(!empty()&&!back())pop_back();
}
void operator +=(const Wint &x){
// print(),putchar('+'),x.print();
if(size()<x.size())resize(x.size());
for(int i=0;i<x.size();i++)(*this)[i]+=x[i];
~*this;
// putchar('='),print(),putchar('\n');
}
friend Wint operator *(const Wint &x,const Wint &y){
// x.print(),putchar('*'),y.print();
Wint z;
if(!x.size()||!y.size())return z;
z.resize(x.size()+y.size()-1);
for(int i=0;i<x.size();i++)for(int j=0;j<y.size();j++)z[i+j]+=x[i]*y[j];
~z;
// putchar('='),z.print(),putchar('\n');
return z;
}
void print()const{
if(empty()){putchar('0');return;}
for(int i=size()-1;i>=0;i--)putchar('0'+(*this)[i]);
}
}f[1010][510][2],h[1010][2];
//f[i,j,0/1]:the number of situations when there're j matchings in subtree i and i itself isn't/is matched; and g is the maximal matchings. h is an array for assistance.
vector<int>v[1010];
bool occ[1010];
void dfs(int x){
f[x][0][0]=1;
for(auto y:v[x]){
dfs(y);
int tmp0=g[x][0]+max(g[y][0],g[y][1]),tmp1=max(g[x][1]+max(g[y][0],g[y][1]),g[x][0]+g[y][0]+1);
for(int i=0;i<=tmp0;i++)h[i][0]=0;
for(int i=0;i<=tmp1;i++)h[i][1]=0;
for(int i=max(0,g[x][0]-1);i<=g[x][0];i++)for(int j=max(0,g[y][0]-1);j<=g[y][0];j++)h[i+j][0]+=f[x][i][0]*f[y][j][0],h[i+j+1][1]+=f[x][i][0]*f[y][j][0];
for(int i=max(0,g[x][1]-1);i<=g[x][1];i++)for(int j=max(0,g[y][0]-1);j<=g[y][0];j++)h[i+j][1]+=f[x][i][1]*f[y][j][0];
for(int i=max(0,g[x][0]-1);i<=g[x][0];i++)for(int j=max(0,g[y][1]-1);j<=g[y][1];j++)h[i+j][0]+=f[x][i][0]*f[y][j][1];
for(int i=max(0,g[x][1]-1);i<=g[x][1];i++)for(int j=max(0,g[y][1]-1);j<=g[y][1];j++)h[i+j][1]+=f[x][i][1]*f[y][j][1];
for(int i=max(0,tmp0-1);i<=tmp0;i++)f[x][i][0]=h[i][0];
for(int i=max(0,tmp1-1);i<=tmp1;i++)f[x][i][1]=h[i][1];
g[x][0]=tmp0;
g[x][1]=tmp1;
}
}
int main(){
scanf("%d",&n);
for(int i=1,x,y,z;i<=n;i++){
scanf("%d%d",&x,&y);
while(y--)scanf("%d",&z),v[x].push_back(z),occ[z]=true;
}
for(int i=1;i<=n;i++){
if(occ[i])continue;
dfs(i);
int mx=max(g[i][0],g[i][1]);
Wint ret;
if(mx==g[i][0])ret+=f[i][g[i][0]][0];
if(mx==g[i][1])ret+=f[i][g[i][1]][1];
printf("%d\n",mx);
ret.print();
}
return 0;
}