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

http://acm.hdu.edu.cn/showproblem.php?pid=4123

题意:给你一棵树,然后标号为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


#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)的做法,好像是单调队列的做法


#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;  
}  



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值