LINK:树的染色
考场上以为这道题要爆蛋了 没想到 推出正解来了.
反正是先写了爆搜的 爆搜最近越写越熟练了
容易想到dp 容易设出状态 f[i][j]表示以i为根的子树内白色的值为j此时黑色的值怎么样。
可以发现 当白色值固定的时候黑色值可能有多个 所以合法不合法这个状态不太行。
可以上f[i][j][k]了 不过这样复杂度极高 转移很暴力 不一定能跑过40.
考虑 对于一个白色颜色和为j来说 那么黑色和 有k1 k2都是合法了 容易得到只有较小的一个才有用。
那么就有状态了 f[i][j]表示以i为根的子树内白色的值为j此时黑色和的最小值。
复杂度(nv^2)
不过对于链的情况 复杂度会进一步达到(nv)可以通过60分。
容易想到 最后的复杂度应该是nv的。
观察状态转移式 可以发现 儿子之中只有两种状态有效 一个是 f[tn][v[tn]] 一个是 f[tn][k]=v[tn]。
前者 只有一个值直接对父亲进行更新即可。后者显然最小k是最优的 也直接对父亲进行贡献即可。
综上复杂度为nv.
const int MAXN=1010;
int T;
int n,maxx,flag,len;
int f[MAXN][5003];//f[i][j]表示以i为根的子树内当白色的和为j时黑色的最小值.
int lin[MAXN],ver[MAXN],v[MAXN],nex[MAXN],g[5003];
inline void add(int x,int y)
{
ver[++len]=y;
nex[len]=lin[x];
lin[x]=len;
}
inline void dp(int x)
{
int mark=0;
go(x)
{
dp(tn);
if(!mark)rep(0,maxx,j)f[x][j]=f[tn][j];
else
{
int ww=INF;
rep(0,maxx,j)
{
g[j]=f[x][j],f[x][j]=INF;
if(f[tn][j]==v[tn]&&ww==INF)ww=j;
}
//rep(0,maxx,j)rep(0,j,k)f[x][j]=min(f[x][j],g[j-k]+f[tn][k]);
//容易想到对于某个儿子 如果其选择白色 显然只有f[v[x]]的状态有效.
//如果选择黑色 存在只有f[tn][j]=v[x]的状态有效.
//可以实现转移降低复杂度. 前者直接转移 后者找到最小的进行转移 可以证明是最优的.
if(f[tn][v[tn]]<INF)
{
rep(0,maxx,j)if(j+v[tn]<=maxx)f[x][j+v[tn]]=min(f[x][j+v[tn]],f[tn][v[tn]]+g[j]);
else break;
}
if(ww!=INF)
{
rep(0,maxx,j)if(j+ww<=maxx)f[x][j+ww]=min(f[x][j+ww],g[j]+f[tn][ww]);
else break;
}
}
mark=1;
}
if(!mark)f[x][v[x]]=0,f[x][0]=v[x];
else
{
rep(0,maxx,i)g[i]=f[x][i],f[x][i]=INF;
rep(0,maxx,i)
{
//当前选择白色
if(i<=v[x])f[x][v[x]]=min(f[x][v[x]],g[i]);
//当前选择黑色
f[x][i]=min(f[x][i],(g[i]<=v[x]?v[x]:INF));
}
}
}
int main()
{
freopen("coloring.in","r",stdin);
freopen("coloring.out","w",stdout);
get(T);
while(T--)
{
get(n);len=0;maxx=5000;flag=0;
rep(1,n,i)
{
lin[i]=0;
memset(f[i],0x3f,sizeof(f[i]));
}
rep(2,n,i)add(read(),i);
rep(1,n,i)get(v[i]);
dp(1);
rep(0,maxx,i)if(f[1][i]<INF)flag=1;
if(flag)puts("POSSIBLE");
else puts("IMPOSSIBLE");
}
return 0;
}