POJ 4003 树形DP+RMQ

题意:

一颗树,得到每个节点可到达的最远路径长度组成的序列,每给定一个q,求最长满足{最大值-最小值<=q}的连续序列的长度

分析:

①求以每个节点出发的最长路径

dfs+树形dp:

dfs出从u节点出发,向下延伸的最长路径的长度dis[u],以及是从那条边向下延伸得到的disnum[u],还有u节点向下延伸的次长路pis[u]

从上向下DP,找到dp[u]表示从u向父节点延伸的最长路径长度

(如果这个还不熟练请移步:HDU 2196,就是求步骤①)

②维护区间最大最小的差

维护一个队列(虚拟的,真实存在的只是 h 队头指针 ,t  队尾指针 两个指针),若当前h~t不满足条件h++

否则t++

用rmq O(1)算出最值

PS:log不预处理出来会TLE,TLE两次,再度听WZC神犇说log巨慢无比,改成预处理就AC了~

XLk神犇说单纯的单调队列可以做,表示我这蒟蒻不会。。。

View Code
  1 #include <cstdio>
  2 #include <cmath>
  3 #include <cstdlib>
  4 #include <cstring>
  5 #include <iostream>
  6 #include <algorithm>
  7 
  8 #define N 50010
  9 #define M 5000000
 10 
 11 using namespace std;
 12 
 13 int to[M],next[M],len[M],head[N],cnt,dis[N],pis[N],ans[N],dp[N],fin,disnum[N],qu,n,m;
 14 int pmax[N][20],pmin[N][20],lg[N*2];
 15 
 16 inline void add(int u,int v,int w)
 17 {
 18     to[cnt]=v; len[cnt]=w; next[cnt]=head[u]; head[u]=cnt++;
 19 }
 20 
 21 void dfs(int u,int pre)
 22 {
 23     dis[u]=pis[u]=0;
 24     for(int i=head[u];~i;i=next[i])
 25         if(pre!=to[i])
 26         {
 27             dfs(to[i],u);
 28             if(dis[u]<dis[to[i]]+len[i])
 29             {
 30                 dis[u]=dis[to[i]]+len[i];
 31                 disnum[u]=to[i];
 32             }
 33         }
 34     for(int i=head[u];~i;i=next[i])
 35         if(pre!=to[i]&&disnum[u]!=to[i])
 36             if(pis[u]<dis[to[i]]+len[i])
 37                 pis[u]=dis[to[i]]+len[i];
 38 }
 39 
 40 void find(int u,int pre)
 41 {
 42     for(int i=head[u];~i;i=next[i])
 43         if(to[i]!=pre)
 44         {
 45             if(to[i]==disnum[u]) dp[to[i]]=max(dp[u],pis[u])+len[i];
 46             else dp[to[i]]=max(dp[u],dis[u])+len[i];
 47             find(to[i],u);
 48         }
 49 }
 50 
 51 int maxrmq(int l,int r)
 52 {
 53     int k=lg[r-l+1];
 54     return max(pmax[l][k],pmax[r-(1<<k)+1][k]);
 55 }
 56 
 57 int minrmq(int l,int r)
 58 {
 59     int k=lg[r-l+1];
 60     return min(pmin[l][k],pmin[r-(1<<k)+1][k]);
 61 }
 62 
 63 void pretend()
 64 {
 65     scanf("%d",&qu);
 66     fin=0;
 67     int h=1,t=1;
 68     while(h<=t&&t<=n)
 69     {
 70         if(maxrmq(h,t)-minrmq(h,t)<=qu)
 71         {
 72             fin=max(fin,t-h+1);
 73             t++;
 74         }
 75         else h++;
 76     }
 77     printf("%d\n",fin);
 78 }
 79 
 80 void init_rmq()
 81 {
 82     for(int i=1;i<=n;i++)
 83         pmax[i][0]=pmin[i][0]=ans[i];
 84     for(int j=1;(1<<j)<=n;j++)
 85         for(int i=1;i+(1<<j)-1<=n;i++)
 86         {
 87             pmax[i][j]=max(pmax[i][j-1],pmax[i+(1<<(j-1))][j-1]);
 88             pmin[i][j]=min(pmin[i][j-1],pmin[i+(1<<(j-1))][j-1]);
 89         }
 90 }
 91 
 92 void go()
 93 {
 94     memset(disnum,-1,sizeof disnum);
 95     memset(dp,-1,sizeof dp);
 96     dfs(1,-1);
 97     dp[1]=0;
 98     find(1,-1);
 99     for(int i=1;i<=n;i++) ans[i]=max(dp[i],dis[i]);
100     init_rmq();
101     for(int i=1;i<=m;i++) pretend();
102 }
103 
104 void read()
105 {
106     memset(head,-1,sizeof head); cnt=0;
107     for(int i=1,a,b,c;i<n;i++)
108     {
109         scanf("%d%d%d",&a,&b,&c);
110         add(a,b,c),add(b,a,c);
111     }
112 }
113 
114 int main()
115 {
116     for(int i=1;i<100000;i++)
117         lg[i]=(i>>lg[i-1]+1)?lg[i-1]+1:lg[i-1];
118     while(scanf("%d%d",&n,&m),n||m)
119     {
120         read();
121         go();
122     }
123     //system("pause");
124     return 0;
125 }
没有人能阻止我前进的步伐,除了我自己!
原文地址:https://www.cnblogs.com/proverbs/p/2720399.html