poj4003 树形dp, rmq_st

很综合的题目,出得非常好

第一个问题是求以树中的每个节点为起点所能走的最长路:

首先,求出各个点的最长路,次长路,以及最长路的后继节点,再据此得到答案

第二个问题,需要rmq,并且维护一个队列,只要满足条件,队尾添加新元素,不然同时弹出队头

注意!dfs函数中tmp2为根走另一分支所得次长路,与最长路无公共路径!


#include <iostream>
using namespace std;
struct gtype {
       int y,d,next;
}g[100010];
int first[50010],tot,x,y,d,s[50010],a[50010][4],n,m,q;
int MIN[50001][20],MAX[50001][20];
void add(int x,int y,int d) {
     tot++;
     g[tot].y = y;
     g[tot].d = d;
     g[tot].next = first[x];
     first[x] = tot;
}
int dfs(int x,int fa) {
    int tmp1 = 0, tmp2 = 0, vx = 0;
    for (int t=first[x];t!=-1;t=g[t].next) {
        int y = g[t].y;
        if (y==fa) continue;
        int tmp = g[t].d + dfs(y, x);
        if (tmp > tmp1) {
                tmp2 = tmp1;
                tmp1 = tmp;
                vx = y;
        }
        else if (tmp > tmp2) tmp2 = tmp;
    }
    a[x][0] = vx;
    a[x][1] = tmp1;
    a[x][2] = tmp2;
    return tmp1;
}     
void find(int x,int fa,int ans) {
     int vx = a[x][0];
     int tmp1 = a[x][1];
     int tmp2 = a[x][2];
     if (ans > tmp1) {
             tmp2 = tmp1;
             tmp1 = ans;
             vx = 0;
     }
     else if (ans > tmp2) tmp2 = ans;
     s[x-1] = tmp1;
     for (int t=first[x];t!=-1;t=g[t].next) {
         int y = g[t].y;
         if (y==fa) continue;
         if (y==vx) find(y,x,tmp2+g[t].d);
         else       find(y,x,tmp1+g[t].d);
     }
}
int max(int x,int y) {
    return s[x]>s[y]?x:y;
}
int min(int x,int y) {
    return s[x]>s[y]?y:x;
}
void ST() {
    int i,j;
    for (i=0;i<=n;i++)        
        MIN[i][0]=MAX[i][0]=i;
    for (j=1;1<<j<=n;j++)
        for (i=0;i+(1<<j)-1<n;i++) {            
                     MIN[i][j]=min(MIN[i][j-1],MIN[i+(1<<(j-1))][j-1]);
                     MAX[i][j]=max(MAX[i][j-1],MAX[i+(1<<(j-1))][j-1]);
        }    
}
int RMQ(int i,int j,int x) {//询问区间[i,j],x==1返回最大值下标,否则返回最小值下标 
    if (i>j) {
             i^=j;j^=i;i^=j;
    }
    int k=0;
    while (i+(1<<k)<j-(1<<k)+1) k++;
    if(x==1)
        return max(MAX[i][k],MAX[j-(1<<k)+1][k]);
    return min(MIN[i][k],MIN[j-(1<<k)+1][k]);
}
int maxi(int a,int b) {
    if (a>b) return a; else return b;
}
int main() {
    while (scanf("%d%d",&n,&m)) {
          if (n==0 && m==0) break;
          tot = 0;
          memset(g,0,sizeof(g));
          memset(first,-1,sizeof(first));
          for (int i=1;i<n;i++) {
              scanf("%d%d%d",&x,&y,&d);
              add(x,y,d);
              add(y,x,d);
          }
          dfs(1,0);
          find(1,0,0);
          ST();
          for (int i=1;i<=m;i++) {
              scanf("%d",&q);
              int head = 1, tail = 1, ans = 0;
              while (tail <= n) {
                    int tmp1 = s[RMQ(head-1,tail-1,0)];
                    int tmp2 = s[RMQ(head-1,tail-1,1)];
                    if (tmp2-tmp1<=q) {
                          tail++;
                          ans = maxi(ans,tail-head);
                    }
                    else { head++;tail++; }
              }
              printf("%d\n",ans);
          }
    }
    return 0;
}


你可能感兴趣的:(ini)