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
");  continue; }
156         int root = 1;
157         tree_dp1(root);
158         //for(int i=1;i<=n;i++) printf("no[%d]: %d	%d,	 %d	%d
",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	%d,	 %d	%d
",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
",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
",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
",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
",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
",dddl(n,m));
124     }
125     return 0;
126 }
View Code
原文地址:https://www.cnblogs.com/karlvin/p/3225848.html