poj 3162 线段树 hdu 4123 bfs + RMQ预处理

来源:岁月联盟 编辑:exp 时间:2012-08-17

题意:给你一棵树,然后标号为1~n,每条边都有一定的边权,那么从每个点出发都有一个最远距离num[i];
先求出num【】数组,然后再有500个询问,每个询问输入一个整数m,求num数组中最大值与最小值绝对值之差不超过m的最长的连续区间是多少。

这是2011福州区域赛的题目,咋一看蛮难的,其实是个大水题,poj也有类似的题目,应该说是改编过来的吧。
好了,讲讲我是怎么做的吧
1:利用树的最长路的结论我们首先预处理出num数组
2:由于后面需要查询区间最值,所以先用RMQ预处理num【】,后面查询的时候就可以做到O(1)查询,如果用线段树的话复杂度会太大
3:对于每个询问,可以做到O(n)回答,维护两个指针l,r 如果当前区间满足条件r++,否则l++,每个点都被插入删除一次,区间最值的查询是O(1)的,所以询问的复杂度是
q*n即500*50000.
总复杂度是nlog(n)+q*n
所以就是q*n的复杂度,即500*50000,时限两秒,小轻松啊~

[cpp] 
#include<cstdio> 
#include<cstring> 
#include<algorithm> 
using namespace std; 
const int inf = ~0u>>2; 
const int maxn = 50010; 
int n,m; 
struct Edge{ 
    int v,w,next; 
}edge[2*maxn]; 
int head[maxn],E,num[maxn]; 
void add_edge(int a,int b,int w) 

    edge[E].v=b; 
    edge[E].w=w; 
    edge[E].next=head[a]; 
    head[a]=E++; 

int dis1[maxn],dis2[maxn]; 
bool vis[maxn]; 
int q[maxn]; 
void bfs(int s,int &ss,int dist[])   { 
    fill(dist,dist+n+1,inf); 
    fill(vis,vis+n+1,false); 
    vis[s]=true; 
    dist[s]=0; 
    int front(0),tail(0),u,v,w; 
    q[0]=s;int Max=0; 
    while(front<=tail)    { 
        u=q[front++]; 
        for(int i=head[u];i!=-1;i=edge[i].next)    { 
            v=edge[i].v,w=edge[i].w; 
            if(vis[v]) continue; 
            vis[v]=true; 
            dist[v]=dist[u]+w; 
            if(dist[v]>Max)      { 
                Max=dist[v]; 
                ss=v; 
            } 
            q[++tail]=v; 
        } 
    } 

int LOG[2*maxn]; 
int dp1[20][2*maxn]; 
int dp2[20][2*maxn]; 
inline int min(int a,int b){ 
    return a<b?a:b; 

inline int max(int a,int b){ 
    return a>b?a:b; 

void rmq_init(int n) 

    int i,j; 
    for(j=1;j<=n;j++)   { 
        dp1[0][j]=num[j]; 
        dp2[0][j]=num[j]; 
    } 
    for(j=1;j<=LOG[n];j++)   { 
        int limit=n+1-(1<<j); 
        for(i=1;i<=limit;i++)    { 
            int x=i+(1<<j>>1); 
            dp1[j][i]=min(dp1[j-1][x],dp1[j-1][i]); 
            dp2[j][i]=max(dp2[j-1][x],dp2[j-1][i]); 
        } 
    } 

int rmq_min(int l,int r) 

    int m=LOG[r-l+1]; 
    return min(dp1[m][l],dp1[m][r-(1<<m)+1]); 

int rmq_max(int l,int r) 

    int m=LOG[r-l+1]; 
    return max(dp2[m][l],dp2[m][r-(1<<m)+1]); 

int main() 

    int q; 
    LOG[0]=-1; 
    for(int i=1;i<2*maxn;i++) LOG[i]=LOG[i>>1]+1; 
    while(scanf("%d%d",&n,&q),n||q) 
    { 
        E=0;fill(head,head+n+1,-1); 
        for(int i=2,a,b,w;i<=n;i++) 
        { 
            scanf("%d%d%d",&a,&b,&w); 
            add_edge(a,b,w); 
            add_edge(b,a,w); 
        } 
        int ss,tt; 
        bfs(1,ss,dis1); 
        bfs(ss,tt,dis1); 
        bfs(tt,ss,dis2); 
        for(int i=1;i<=n;i++)     num[i]=max(dis1[i],dis2[i]); 
        rmq_init(n); 
        while(q--)    { 
            scanf("%d",&m); 
            int ans=0; 
            int l=1,r=1,mx,mi; 
            while(r<=n)    { 
                mx=rmq_max(l,r); 
                mi=rmq_min(l,r); 
                if(mx-mi<=m)    { 
                    ans=max(ans,r-l+1); 
                    r++; 
                } 
                else l++; 
            } 
            printf("%d/n",ans); 
        } 
    } 
    return 0; 

http://poj.org/problem?id=3162
poj 3162这题呢有100W个点啊,RMQ的话空间开不下,所以只能线段树查询区间最值了,复杂度n*log(n),discuss说还有O(n)的做法,好像是单调队列的做法,还不会,等会了再贴上来吧。
c++跑了5 S 多

[cpp] 
#include<cstdio> 
#include<set> 
#include<cstring> 
#include<algorithm> 
using namespace std; 
#define lson l,m,rt<<1 
#define rson m+1,r,rt<<1|1 
const int inf = ~0u>>2; 
const int maxn = 1000010; 
int n,m; 
struct Edge{ 
    int v,w,next; 
}edge[2*maxn]; 
int head[maxn],E,num[maxn]; 
void add_edge(int a,int b,int w) 

    edge[E].v=b; 
    edge[E].w=w; 
    edge[E].next=head[a]; 
    head[a]=E++; 

int dis1[maxn],dis2[maxn]; 
bool vis[maxn]; 
int q[maxn]; 
void bfs(int s,int &ss,int dist[])   { 
    fill(dist,dist+n+1,inf); 
    fill(vis,vis+n+1,false); 
    vis[s]=true; 
    dist[s]=0; 
    int front(0),tail(0),u,v,w; 
    q[0]=s;int Max=0; 
    while(front<=tail)   { 
        u=q[front++]; 
        for(int i=head[u];i!=-1;i=edge[i].next) { 
              v=edge[i].v,w=edge[i].w; 
              if(vis[v]) continue; 
              vis[v]=true; 
              dist[v]=dist[u]+w; 
              if(dist[v]>Max)      { 
                  Max=dist[v]; 
                  ss=v; 
              } 
              q[++tail]=v; 
        } 
    } 

inline int min(int a,int b){ 
    return a<b?a:b; 

inline int max(int a,int b){ 
    return a>b?a:b; 

int mx[maxn<<2],mi[maxn<<2]; 
void pushup(int rt){ 
    mx[rt]=max(mx[rt<<1],mx[rt<<1|1]); 
    mi[rt]=min(mi[rt<<1],mi[rt<<1|1]); 

void build(int l,int r,int rt){ 
    if(l==r){ 
        mx[rt]=mi[rt]=num[l]; 
        return ; 
    } 
    int m=(l+r)>>1; 
    build(lson); 
    build(rson); 
    pushup(rt); 

int find_min(int L,int R,int l,int r,int rt) 

    if(L<=l&&r<=R) 
    { 
        return mi[rt]; 
    } 
    int m=(l+r)>>1; 
    int ret=inf; 
    if(L<=m) ret=min(ret,find_min(L,R,lson)); 
    if(R>m) ret=min(ret,find_min(L,R,rson)); 
    return ret; 

int find_max(int L,int R,int l,int r,int rt) 

    if(L<=l&&r<=R) 
    { 
        return mx[rt]; 
    } 
    int m=l+r>>1; 
    int ret=0; 
    if(L<=m) ret=max(ret,find_max(L,R,lson)); 
    if(R>m) ret=max(ret,find_max(L,R,rson)); 
    return ret; 

int main() 

    scanf("%d%d",&n,&m); 
    E=0;fill(head,head+n+1,-1); 
    for(int i=2,fa,w;i<=n;i++) 
    { 
        scanf("%d%d",&fa,&w); 
        add_edge(i,fa,w); 
        add_edge(fa,i,w); 
    } 
    int ss,tt; 
    bfs(1,ss,dis1); 
    bfs(ss,tt,dis1); 
    bfs(tt,ss,dis2); 
    for(int i=1;i<=n;i++)  
    { 
        num[i]=max(dis1[i],dis2[i]); 
    } 
    int l=1,r=1,mx,mi; 
    int ans=0; 
    build(1,n,1); 
    while(r<=n) 
    { 
        mx=find_max(l,r,1,n,1); 
        mi=find_min(l,r,1,n,1); 
        if(mx-mi<=m) 
        { 
            ans=max(ans,r-l+1); 
            r++; 
        } 
        else { 
         
            l++; 
        } 
    } 
    printf("%d/n",ans); 
    return 0;