[关闭]
@ysner 2018-07-19T14:57:07.000000Z 字数 3185 阅读 1835

藏宝

DP 二分


题面

给一棵有个节点的带点权树,有次询问,问如果要求一条路径上点权和小于等于,那么这条路径最多能包括多少点?

解析

算法

从每一个点出发,看最多能走多远。
注意存在的情况!!!
复杂度上限

算法

直接暴力
表示自点向下,点权和为时最多能向下走多远。
表示以点为根的子树中,点权和为的路径最多有多长。
转移方程



统计所有最大值,就可以处理询问了。
复杂度
注意:
1、对一点进行时,该点数组中不能包含当前子节点的贡献。
2、注意边界是叶节点。(这玩意儿应该写在开头)
3、数组后面答案要与前面取

  1. il void dfs(re int u,re int fa)
  2. {
  3. re int flag=0;
  4. for(re int i=h[u];i+1;i=e[i].nxt)
  5. {
  6. re int v=e[i].to;
  7. if(v==fa) continue;flag=1;
  8. dfs(v,u);
  9. fp(i,0,20) fp(j,0,i) f[u][i]=max(f[u][i],g[u][j]+g[v][i-j]);
  10. fp(i,0,20-w[u]) g[u][i+w[u]]=max(g[u][i+w[u]],g[v][i]+1);
  11. }
  12. if(!flag) if(w[u]<=20) g[u][w[u]]=1;
  13. }
  14. il void dfss(re int u,re int fa,re ll s,re int dis)
  15. {
  16. if(s<=lim) ans1=max(ans1,dis);
  17. for(re int i=h[u];i+1;i=e[i].nxt)
  18. {
  19. re int v=e[i].to;
  20. if(v==fa) continue;
  21. if(s+w[v]<=lim) dfss(v,u,s+w[v],dis+1);
  22. }
  23. }
  24. int main()
  25. {
  26. memset(h,-1,sizeof(h));
  27. n=gi();Q=gi();
  28. fp(i,1,n) w[i]=gi();
  29. fp(i,1,n-1)
  30. {
  31. re int u=gi(),v=gi();
  32. add(u,v);add(v,u);
  33. }
  34. if(n<=100&&Q<=4000)
  35. {
  36. fp(i,1,Q)
  37. {
  38. ans1=0;lim=gi();
  39. fp(i,1,n) dfss(i,i,w[i],1);
  40. printf("%lld\n",ans1);
  41. }
  42. return 0;
  43. }
  44. dfs(1,0);
  45. fp(i,0,20) fp(j,1,n) ans[i]=max(ans[i],f[j][i]);
  46. fp(i,1,20) ans[i]=max(ans[i],ans[i-1]);
  47. while(Q--)
  48. {
  49. printf("%lld\n",ans[gi()]);
  50. }
  51. return 0;
  52. }

算法

随机数据等价于
有一个套路:
如果一个数组,状态范围大而结果范围小,可以将结果与状态交换位置。
于是我们可以把表示的状态改为以点为根的子树中,包含点数为的路径需要的最小点权和。同理。
转移差不多。并且由于数组不需要上传,这一维实际上可以省掉。
这样就可以处理很大的情况了。
当然,询问时,由于链长越大需要的点权和也越大,符合单调性,可以二分答案。
复杂度左右。

算法

树高很大。
复杂度可以达到,会的。
然而每个点枚举链长时,不一定要枚到。不是每个点下面都有个点。
于是只要枚到该点离自己子树下叶节点最远距离即可。
复杂度?玄学。。。反正能过。

注意一下二分时,判断条件应该是合法或不合法,不能两者混杂。
比如说不能写成

  1. while(l<=r)
  2. {
  3. re ll mid=l+r>>1;
  4. if(f[mid]>=lim) ans=mid,r=mid-1;
  5. else l=mid+1;
  6. }

还有鬼知道为什么数据会有答案大于的。。。
其实是出题人认为树根深度为。。。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstdlib>
  4. #include<cmath>
  5. #include<cstring>
  6. #include<algorithm>
  7. #define ll long long
  8. #define re register
  9. #define il inline
  10. #define ls x<<1
  11. #define rs x<<1|1
  12. #define max(a,b) ((a)>(b)?(a):(b))
  13. #define min(a,b) ((a)<(b)?(a):(b))
  14. #define fp(i,a,b) for(re int i=a;i<=b;i++)
  15. #define fq(i,a,b) for(re int i=a;i>=b;i--)
  16. using namespace std;
  17. const int N=2e4+100;
  18. struct Edge{int to,nxt;}e[N<<1];
  19. ll w[N],n,h[N],cnt,Q,lim,ans;
  20. ll f[2005],g[N][1005],d[N];
  21. il void add(re int u,re int v){e[++cnt]=(Edge){v,h[u]};h[u]=cnt;}
  22. il ll gi()
  23. {
  24. re ll x=0,t=1;
  25. re char ch=getchar();
  26. while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
  27. if(ch=='-') t=-1,ch=getchar();
  28. while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  29. return x*t;
  30. }
  31. il void dfs(re int u,re int fa)
  32. {
  33. d[u]=1;g[u][0]=0;g[u][1]=w[u];
  34. for(re int i=h[u];i+1;i=e[i].nxt)
  35. {
  36. re int v=e[i].to;
  37. if(v==fa) continue;
  38. dfs(v,u);
  39. fp(i,0,d[u]) fp(j,0,d[v]) f[i+j]=min(f[i+j],g[u][i]+g[v][j]);
  40. d[u]=max(d[u],d[v]+1);
  41. fp(i,1,d[u]) g[u][i]=min(g[u][i],g[v][i-1]+w[u]);
  42. }
  43. }
  44. int main()
  45. {
  46. freopen("erusaert.in","r",stdin);
  47. freopen("erusaert.out","w",stdout);
  48. memset(h,-1,sizeof(h));memset(f,63,sizeof(f));memset(g,63,sizeof(g));
  49. n=gi();Q=gi();
  50. fp(i,1,n) w[i]=gi();
  51. fp(i,1,n-1)
  52. {
  53. re int u=gi(),v=gi();
  54. add(u,v);add(v,u);
  55. }
  56. dfs(1,0);
  57. while(Q--)
  58. {
  59. lim=gi();
  60. re ll l=0,r=2000,ans=0;
  61. while(l<=r)
  62. {
  63. re ll mid=l+r>>1;
  64. if(f[mid]<=lim) ans=mid,l=mid+1;
  65. else r=mid-1;
  66. }
  67. printf("%lld\n",ans);
  68. }
  69. fclose(stdin);
  70. fclose(stdout);
  71. return 0;
  72. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注