NOIP2018保卫王国
题目大意:给一颗有点权的树,每次规定两个点选还是不选,求这棵树的最小权点覆盖。
题解
ZZ码农题。
要用动态dp做,这题就是板子,然鹅并不会,留坑代填。
因为没有修改,所以可以静态倍增。
我们先做一遍正常的树形dp,求出g[i][0/1],0/1表示当前节点选或不选。
然后我们再倒腾出一个数组l[i][0/1]表示从当前点作为根,再扣掉当前子树的答案。
然后倍增处理dp[i][j][0/1][0/1]表示从i向上2i长度的链,起点和终点的选择情况,表示以下区域的答案。
比如这条黑色的链,它表示的是黄圈里的所有点的答案。
然后对于一个询问,我们可以跳LCA,边跳变统计答案,这样我们就可以统计出以LCA为根的子树的答案,在加上之前处理过的l数组的答案,就可以吧答案算全了。
NOIP考这种****题有意思吗?
代码
#include<iostream> #include<cstdio> #define N 100009 using namespace std; typedef long long ll; int tot,head[N],deep[N],p[N][20],n,m; char typef[5]; ll dp[N][19][2][2],w[N],f[2][2],g[2][2],pr[N][2],lian[N][2],pr2[N][2]; inline int rd(){ int x=0;char c=getchar();bool f=0; while(!isdigit(c)){if(c=='-')f=1;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();} return f?-x:x; } struct node{int n,to;}e[N<<1]; inline void add(int u,int v){e[++tot].n=head[u];e[tot].to=v;head[u]=tot;} inline void hb(int x,int y,int l){ dp[x][l][0][0]=min(dp[x][l-1][0][1]+dp[y][l-1][1][0],dp[x][l-1][0][0]+dp[y][l-1][0][0]); dp[x][l][1][0]=min(dp[x][l-1][1][1]+dp[y][l-1][1][0],dp[x][l-1][1][0]+dp[y][l-1][0][0]); dp[x][l][0][1]=min(dp[x][l-1][0][1]+dp[y][l-1][1][1],dp[x][l-1][0][0]+dp[y][l-1][0][1]); dp[x][l][1][1]=min(dp[x][l-1][1][1]+dp[y][l-1][1][1],dp[x][l-1][1][0]+dp[y][l-1][0][1]); } void dfs(int u,int fa){ for(int i=1;(1<<i)<=deep[u];++i){ p[u][i]=p[p[u][i-1]][i-1]; hb(u,p[u][i-1],i); } for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){ int v=e[i].to;deep[v]=deep[u]+1;p[v][0]=u; dp[v][0][0][1]=pr[u][1]-min(pr[v][1],pr[v][0]); dp[v][0][1][0]=pr[u][0]-pr[v][1]; dp[v][0][1][1]=pr[u][1]-min(pr[v][1],pr[v][0]); dfs(v,u); } } void predfs(int u,int fa){ pr[u][0]=0;pr[u][1]=w[u]; for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){ int v=e[i].to;predfs(v,u); pr[u][0]+=pr[v][1];pr[u][1]+=min(pr[v][0],pr[v][1]); } } void dfs2(int u,int fa){ for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){ int v=e[i].to; // pr2[v][0]=pr2[u][1];pr2[v][1]=min(pr2[u][0],pr2[u][1]); // lian[v][0]=pr2[v][0]-pr[v][0];lian[v][1]=pr2[v][1]-pr[v][1]; lian[v][0]=lian[u][1]+pr[u][1]-min(pr[v][0],pr[v][1]); lian[v][1]=min(lian[v][0],lian[u][0]+pr[u][0]-pr[v][1]); dfs2(v,u); } } inline ll getlca(int a,int b,int x,int y){ if(deep[a]<deep[b])swap(a,b),swap(x,y); // cout<<a<<" "<<b<<" "<<x<<" "<<y<<endl; ll ans=pr[a][x]; //cout<<ans<<endl; f[0][x]=0;int now=1,pre=0; for(int i=18;i>=0;--i)if(deep[a]-(1<<i)>=deep[b]){ f[now][1]=min(f[pre][0]+dp[a][i][0][1],f[pre][1]+dp[a][i][1][1]); f[now][0]=min(f[pre][0]+dp[a][i][0][0],f[pre][1]+dp[a][i][1][0]); swap(now,pre);a=p[a][i]; } if(a==b)return ans+f[pre][y]+lian[a][y]; g[pre][y]=0;ans+=pr[b][y];//cout<<ans<<endl; for(int i=18;i>=0;--i)if(p[a][i]!=p[b][i]){ f[now][1]=min(f[pre][0]+dp[a][i][0][1],f[pre][1]+dp[a][i][1][1]); f[now][0]=min(f[pre][0]+dp[a][i][0][0],f[pre][1]+dp[a][i][1][0]); g[now][1]=min(g[pre][0]+dp[b][i][0][1],g[pre][1]+dp[b][i][1][1]); g[now][0]=min(g[pre][0]+dp[b][i][0][0],g[pre][1]+dp[b][i][1][0]); swap(now,pre);a=p[a][i];b=p[b][i]; } swap(now,pre); ll ans1=f[now][1]+g[now][1]+pr[p[a][0]][0]-pr[a][1]-pr[b][1]; ll ans2=min(f[now][1],f[now][0])+min(g[now][0],g[now][1])+pr[p[a][0]][1]-min(pr[a][0],pr[a][1])-min(pr[b][0],pr[b][1]); return ans+min(ans1+lian[p[a][0]][0],ans2+lian[p[a][0]][1]); } int main(){ n=rd();m=rd();scanf("%s",typef);int u,v; for(int i=1;i<=n;++i)w[i]=rd(); for(int i=1;i<=n;++i) for(int j=0;j<=18;++j) for(int k=0;k<=1;++k)for(int l=0;l<=1;++l)dp[i][j][k][l]=1e12; for(int i=1;i<n;++i){u=rd();v=rd();add(u,v);add(v,u);} predfs(1,0); pr2[1][1]=pr[1][1];pr2[1][0]=pr[1][0];dfs2(1,0); dfs(1,0);int a,x,b,y; /* for(int i=1;i<=n;++i){ cout<<i<<" "; for(int j=0;j<=1;++j)cout<<lian[i][j]<<" ";cout<<endl; }*/ /* for(int i=1;i<=n;++i){ cout<<i<<endl; for(int j=0;(1<<j)<=deep[i];++j)cout<<dp[i][j][0][0]<<" "<<dp[i][j][0][1]<<" "<<dp[i][j][1][0]<<" "<<dp[i][j][1][1]<<endl; }*/ while(m--){ a=rd();x=rd();b=rd();y=rd(); for(int i=0;i<=1;++i)for(int j=0;j<=1;++j)f[i][j]=g[i][j]=1e12; ll ans=getlca(a,b,x,y); if(ans>1e10)puts("-1");else printf("%lld\n",ans); } return 0; }