题目大意:给一个树形图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 }
单调队列核心:最大值的单调队列保持递减,最小值得单调队列保持递增!然后记录队列中每个最值的下标,使用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 }