poj3162 Walking Race

题目大意:给一个树形图n个点(n-1条边),XXX要练习竞走,每次选定一个点k作为开始点,每次走从k开始能走的最长的一条路径(不要重边)。要求出最长的连续的这样的k,假设连续有kx个,前提:这样kx条路径的长度的任意两个值的差不超过m。

输入: n,m; 接下来n-1行数据,每一行两个整数fi,di。第i行表示 第i+1个点与fi连着一条长度为di的边。

不用想,稍微有一点点水平的朋友都能想到,首先对于每一个点,用一个树形dp求每个点能走到的最长路径长。

树形dp

  定义结构体: max1,max1from, max2。 //记录跟这个点有关的 最长路径长、最长路径长点, 第二最长路径长.

  tree_dp1求出只走子树的边能到达的 max1,max1from, max2,max2from。

  tree_dp2补上求出加上走向父节点的边能到达的 max1,max1from, max2。

第一步还是挺简单的,相信大多数人YY一下就可以搞定的。这样从每个点开始能走到的最长路径长maxlen就出来了。这个效率是O(n)的

然后需要做的是: 求出一个最长的连续区间,对这样区间上任意两个点i,j有 |maxlen[i] - maxlen[j]| <= m。

单调队列

  /**************************************************/

  大家仔细看看这些星号就知道了。

  (O(∩_∩)O~跟你开玩笑的,还是看163到179行的代码吧)

当然第二步也是很简单的,只是要写个水水的线段树作辅助。。。。这个效率最坏O(n)*log(n)+O(n)。

PS:个人觉得这第二步可以试试枚举起点,然后二分搜索区间终点,由于线段树查询效率为log(n),所以总效率O(n)*log(n)*log(n),再加上一些剪枝可以卡过的,可是……

没有可是了,擦擦,贡献了n个WA。

献上代码,与大家共勉!

  1 #include <iostream>

  2 #include <cstring>

  3 #include <cstdio>

  4 #include <algorithm>

  5 #include <vector>

  6 #include <stack>

  7 using namespace std;

  8 const int maxn = 1000100;

  9 const int maxe = maxn*2;

 10 const int maxf = (1<<31)^(-1);

 11 

 12 struct edge{

 13        int u,v,c;

 14        edge(int a=0,int b=0,int d=0){ u=a, v=b, c=d; }

 15 }e[maxe];

 16 int head[maxn];

 17 int next[maxe];

 18 int cnt;

 19 void add(int u,int v,int c){

 20      e[cnt] = edge(u,v,c);

 21      next[cnt] = head[u], head[u] = cnt++;

 22 }

 23 struct node{

 24     int p, plen;

 25     int max1,max1from;

 26     int max2,max2from;

 27 }no[maxn];

 28 stack<int> iden;

 29 int visited[maxn];

 30 void tree_dp1(int root){

 31     memset(visited,0,sizeof(visited));

 32     while(!iden.empty()) iden.pop();

 33     iden.push(root);

 34     int u,v=root;

 35     no[root].p = 0, no[root].plen=0;

 36     no[v].max1 = no[v].max2 = 0;

 37     no[v].max1from = no[v].max2from = -1;

 38     while(!iden.empty()){

 39         u = iden.top();

 40         if(!visited[u]){

 41             for(int i=head[u];i>=0;i=next[i]){

 42                 v = e[i].v;

 43                 if(v == no[u].p) continue;

 44                 no[v].p = u; no[v].plen = e[i].c;

 45                 no[v].max1 = no[v].max2 = 0;

 46                 no[v].max1from = no[v].max2from = -1;

 47                 iden.push(v);

 48             }

 49         }

 50         else{ //当前点u访问完毕

 51             iden.pop();

 52             for(int i=head[u];i>=0;i=next[i]){

 53                 v = e[i].v;

 54                 if(v == no[u].p) continue;

 55                 int len = e[i].c + no[v].max1;

 56                 if(len > no[u].max1){

 57                     no[u].max2 = no[u].max1, no[u].max2from = no[u].max1from;

 58                     no[u].max1 = len; no[u].max1from = v;

 59                 }

 60                 else {

 61                     if(len > no[u].max2)

 62                         no[u].max2 = len, no[u].max2from = v;

 63                 }

 64             }

 65         }

 66         visited[u] = 1;

 67     }

 68 }

 69 int maxlen[maxn];

 70 void tree_dp2(int root){

 71     while(!iden.empty()) iden.pop();

 72     iden.push(root);

 73     int u,v;

 74     while(!iden.empty()){

 75         u = iden.top(); iden.pop();

 76         for(int i=head[u];i>=0;i=next[i]){

 77             v = e[i].v;

 78             if(v == no[u].p) continue;

 79             iden.push(v);

 80         }

 81         maxlen[u] = no[u].max1;

 82         if(u != root){

 83             int p = no[u].p,len;

 84             if(no[p].max1from != u)

 85                 len = no[p].max1+no[u].plen;

 86             else

 87                 len = no[p].max2+no[u].plen;

 88             maxlen[u] = max(maxlen[u],len);

 89             if(len > no[u].max1){

 90                 no[u].max2 = no[u].max1, no[u].max2from = no[u].max1from;

 91                 no[u].max1 = len; no[u].max1from = p;

 92             }

 93             else {

 94                 if(len > no[u].max2)

 95                  no[u].max2 = len, no[u].max2from = p;

 96             }

 97         }

 98     }

 99 }

100 void initial(){

101     cnt = 0;

102     memset(head,-1,sizeof(head));

103 }

104 struct segment{

105     int left,right;

106     int maxv,minv;

107 }a[maxn*4];

108 void build(int left,int right,int k){

109     a[k].left=left, a[k].right=right;

110     a[k].minv = a[k].maxv = maxlen[left];

111     if(left < right){

112         int mid=(left+right)>>1,k2=k<<1;

113         build(left,mid,k2);

114         build(++mid,right,k2+1);

115         a[k].minv = min(a[k2].minv,a[k2+1].minv);

116         a[k].maxv = max(a[k2].maxv,a[k2+1].maxv);

117     }

118 }

119 int query_min(int l,int r,int k){

120     if(l <= a[k].left && r >= a[k].right)

121         return a[k].minv;

122     int mid=(a[k].left+a[k].right)>>1,k2=k<<1;

123     int k3=k2+1;

124     if(r <= mid)

125         return query_min(l,r,k2);

126     else {

127         if(l > mid)

128             return query_min(l,r,k3);

129         //else

130             return min(query_min(l,mid,k2),query_min(mid+1,r,k3));

131     }

132 }

133 int query_max(int l,int r,int k){

134     if(l <= a[k].left && r >= a[k].right)

135         return a[k].maxv;

136     int mid=(a[k].left+a[k].right)>>1,k2=k<<1;

137     int k3=k2+1;

138     if(r <= mid)

139         return query_max(l,r,k2);

140     else {

141         if(l > mid)

142             return query_max(l,r,k3);

143         //else

144             return max(query_max(l,mid,k2),query_max(mid+1,r,k3));

145     }

146 }

147 int main()

148 {

149     int n,m;

150     while(scanf("%d%d",&n,&m) != EOF){

151         initial();

152         int fi,di;

153         for(int i=1;i<n;i++)

154             scanf("%d%d",&fi,&di), add(i+1,fi,di), add(fi,i+1,di);

155         //if(m < 0){ printf("0\n");  continue; }

156         int root = 1;

157         tree_dp1(root);

158         //for(int i=1;i<=n;i++) printf("no[%d]: %d\t%d,\t %d\t%d\n",i,no[i].max1,no[i].max1from,no[i].max2,no[i].max2from);

159         tree_dp2(root);

160         build(1,n,1);

161         //for(int i=1;i<=n;i++) printf("no[%d]: %d\t%d,\t %d\t%d\n",i,no[i].max1,no[i].max1from,no[i].max2,no[i].max2from);

162         //for(int i=1;i<=n;i++) printf("maxlen[%d] = %d\n",i,maxlen[i]);

163         int ret = 1;

164         int left=1,right=1;

165         int tpmin=maxlen[1],tpmax=maxlen[1];

166         while(left<=right && right<=n){

167             if(tpmax-tpmin <= m){

168                 ret = max(ret,right-left+1);

169                 right++;

170                 tpmin = min(tpmin,maxlen[right]);

171                 tpmax = max(tpmax,maxlen[right]);

172             }

173             else {

174                 left++;

175                 tpmin = query_min(left,right,1);

176                 tpmax = query_max(left,right,1);

177             }

178             if(ret > n - left) break;

179         }

180         printf("%d\n",ret);

181     }

182     return 0;

183 }
View Code

 
  单调队列核心:最大值的单调队列保持递减,最小值得单调队列保持递增!然后记录队列中每个最值的下标,使用left、right游标追赶。下面来个个人珍藏版代码。

  1 #include <iostream>

  2 #include <cstring>

  3 #include <cstdio>

  4 #include <algorithm>

  5 #include <vector>

  6 #include <stack>

  7 using namespace std;

  8 const int maxn = 1000100;

  9 const int maxe = maxn*2;

 10 const int maxf = (1<<31)^(-1);

 11 

 12 struct edge{

 13        int u,v,c;

 14        edge(int a=0,int b=0,int d=0){ u=a, v=b, c=d; }

 15 }e[maxe];

 16 int head[maxn];

 17 int next[maxe];

 18 int cnt;

 19 void add(int u,int v,int c){

 20      e[cnt] = edge(u,v,c);

 21      next[cnt] = head[u], head[u] = cnt++;

 22 }

 23 void initial(){

 24     cnt = 0;

 25     memset(head,-1,sizeof(head));

 26 }

 27 

 28 struct node{

 29     int max1,max2; int max1from;

 30     int p,pd,sc;

 31 }no[maxn];

 32 int visited[maxn];

 33 void tree_dp1(int n,int root){

 34     for(int i=1;i<=n;i++) visited[i]=0;

 35     stack<int> st; st.push(root);

 36     int u,v,d;

 37     while(!st.empty()){

 38         u=st.top();

 39         //printf("u = %d, visited[u] = %d\n",u,visited[u]);

 40         if(!visited[u]){

 41             for(int i=head[u];i>=0;i=next[i]){

 42                 v=e[i].v;

 43                 if(v == no[u].p) continue;

 44                 no[v].p=u, no[v].pd=e[i].c;

 45                 st.push(v);

 46             }

 47             visited[u]=1;

 48         }else {

 49             for(int i=head[u];i>=0;i=next[i]){

 50                 v=e[i].v;

 51                 if(v == no[u].p) continue;

 52                 d=e[i].c+no[v].max1;

 53                 if(d >= no[u].max1)

 54                     no[u].max2=no[u].max1, no[u].max1=d, no[u].max1from=v;

 55                 else if(d > no[u].max2)

 56                     no[u].max2=d;

 57             }

 58             st.pop();

 59         }

 60     }

 61 }

 62 void tree_dp2(int n,int root){

 63     for(int i=1;i<=n;i++) visited[i]=0;

 64     stack<int> st; st.push(root);

 65     int u,v,p,d;

 66     while(!st.empty()){

 67         u=st.top();

 68         //printf("u = %d, visited[u] = %d\n",u,visited[u]);

 69         if(!visited[u]){

 70             for(int i=head[u];i>=0;i=next[i])

 71             if(e[i].v != no[u].p)

 72                 st.push(e[i].v);

 73             if(u != root){

 74                 p = no[u].p;

 75                 d = (no[p].max1from==u)?no[p].max2:no[p].max1;

 76                 d += no[u].pd;

 77                 if(d > no[u].max1)  no[u].max2=no[u].max1, no[u].max1=d, no[u].max1from=p;

 78                 else if(d > no[u].max2) no[u].max2=d;

 79             }

 80             visited[u]=1;

 81         }else

 82             st.pop();

 83     }

 84 }

 85 int maxlen[maxn];

 86 int dddl(int n,int M){

 87     if(n <= 0) return n;

 88     if(M < 0) return 0;

 89     deque<int> qmax,qmin; deque<int> idmax,idmin;

 90     int ans=0;

 91     int left=1,right=1;

 92     while(right <= n){

 93         while(!qmax.empty() && maxlen[right] >= qmax.back())

 94             qmax.pop_back(), idmax.pop_back();

 95         qmax.push_back(maxlen[right]); idmax.push_back(right);

 96 

 97         while(!qmin.empty() && maxlen[right] <= qmin.back())

 98             qmin.pop_back(), idmin.pop_back();

 99         qmin.push_back(maxlen[right]); idmin.push_back(right);

100         while(qmax.front()-qmin.front() > M && left<right){

101             left++;

102             while(idmax.front() < left) idmax.pop_front(), qmax.pop_front();

103             while(idmin.front() < left) idmin.pop_front(), qmin.pop_front();

104         }

105         ans = max(ans,right-left+1);

106         right++;

107     }

108     return ans;

109 }

110 int main()

111 {

112     int n,m;

113     while(scanf("%d%d",&n,&m) != EOF){

114         initial();

115         int fi,di;

116         for(int i=1;i<n;i++)

117             scanf("%d%d",&fi,&di), add(i+1,fi,di), add(fi,i+1,di);

118         int root=1;

119         tree_dp1(n,root);

120         tree_dp2(n,root);

121         for(int i=1;i<=n;i++)

122             maxlen[i]=no[i].max1;

123         printf("%d\n",dddl(n,m));

124     }

125     return 0;

126 }
View Code

 

你可能感兴趣的:(poj)