HDU 4123(两种方法-RMQ,单调队列)

题意:50000个点的树,每个点有一个人,每个人会跑到离自己初始点距离最远的点上,这个距离为distance[i]。给你500个查询,对于每个查询Q,找一段连续编号的人,比如[left,right],满足 max( distance[i]  i∈[left,right] ) – min( distance[i]  i∈[left,right] ) ≤ Q,并且使得length=right-left+1要最大,求这个最大的length

法一:关键是O(1)的RMQ。。。。

分析:求distance[i]数组(代码中的d),可以用搜边的方法来解决,然后线性维护一个队列,使得队列中的最大值 – 最小值 ≤ Q,之后,会有一个新的distance[i]插到队列的头部,这样会打破“最大值 – 最小值 ≤ Q”的规则,此时要从队列末尾弹出元素,直到重新满足规则。很明显,维护队列的时候要不断查询某一区间的最大值和最小值,这个可以用RMQ来解决。对于每个distance[i],最多进队列和出队列一次,所以总复杂度为O(nlogn+mn),这貌似有点大啊。

此题还必须有一个极端的优化(当时赛后才想到这个,悲剧。):由于维护队列的时候要不断查询某一区间的最大值和最小值,而log函数慢的一比,所以,要预处理log数组

#include<cstdlib>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
#include<list>
#include<queue>
#include<stack>
#include<vector>
#define tree int o,int l,int r
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define lo o<<1
#define ro o<<1|1
#define pb push_back
#define mp make_pair
#define ULL unsigned long long
#define LL long long
#define inf 0x7fffffff
#define eps 1e-7
#define N 50009
using namespace std;
int m,n,T,t,x,y,z;
vector<pair<int,int> >g[N];
int f[N],s[N],sub[N];
int minv[N][17],maxv[N][17];
int lg[N];
void init()
{
    for (int i=0; i<=n; ++i )
        g[i].clear();
}
void dfs1(int u,int fa)
{
    f[u]=s[u]=0;
    for(int i=0; i<g[u].size(); i++)
    {
        int v=g[u][i].first;
        int l=g[u][i].second;
        if(v!=fa)
        {
            dfs1(v,u);
            if(f[u]<f[v]+l)
            {
                s[u]=f[v]+l;
                swap(s[u],f[u]);
                sub[u]=v;
            }
            else if(s[u]<f[v]+l)
            {
                s[u]=f[v]+l;
            }
        }
    }
}
void dfs2(int u,int fa)
{
    for(int i=0; i<g[u].size(); i++)
    {
        int v=g[u][i].first;
        int l=g[u][i].second;
        if(v!=fa)
        {
            int val;
            if(sub[u]==v)
                val=s[u];
            else
                val=f[u];
            val+=l;
            if(f[v]<val)
            {
                s[v]=val;
                swap(s[v],f[v]);
                sub[v]=u;
            }
            else if(s[v]<val)
            {
                s[v]=val;
            }
            dfs2(v,u);
        }
    }
}
void rmqinit()
{
    for(int i=1; i<=n; i++)
        minv[i][0]=maxv[i][0]=f[i];
    for(int j=1; (1<<j)<=n; j++)
        for(int i=1; i+(1<<j)-1<=n; i++)
        {
            minv[i][j]=min(minv[i][j-1],minv[i+(1<<(j-1))][j-1]);
            maxv[i][j]=max(maxv[i][j-1],maxv[i+(1<<(j-1))][j-1]);
        }
}
int rmqx(int l,int r)
{
    int len=lg[r-l+1];
    return min(minv[l][len],minv[r-(1<<len)+1][len]);
}
int rmqd(int l,int r)
{
    int len=lg[r-l+1];
    return max(maxv[l][len],maxv[r-(1<<len)+1][len]);
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("ex.in","r",stdin);
#endif
    for(int i=1,j=0; i<N; i++)//预处理使RMQ的复杂度变为O(1)
    {
        if(i<=(1<<(j+1)))
            lg[i]=j;
        else
            lg[i]=++j;
    }
    while(scanf("%d%d%*c",&n,&m)==2)
    {
        if(!n&&!m)return 0;
        init();
        for (int i=0; i<n-1; ++i )
        {
            scanf("%d%d%d",&x,&y,&z);
            g[x].pb(mp(y,z));
            g[y].pb(mp(x,z));
        }
        dfs1(1,-1);
        dfs2(1,-1);
        rmqinit();//O(nlgn)
        while(m--)//O(mn)
        {
            int q,ans=1;
            scanf("%d",&q);
            int d=0,x=inf,j=1;
            for(int i=1; i<=n; i++)
            {
                d=max(d,f[i]);
                x=min(x,f[i]);
                if(d-x>q)
                {
                    j++;
                    int xiao=rmqx(j,i);//O(1)
                    int da=rmqd(j,i);
                    while(da-xiao>q)//j==i时会停
                    {
                        j++;
                        xiao=rmqx(j,i);
                        da=rmqd(j,i);
                    }
                    d=da;///////////
                    x=xiao;
                }
                ans=max(ans,i-j+1);
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}
View Code

 

法二:两个单调队列(一个维护最大,一个维护最小)

#include<cstdlib>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
#include<list>
#include<queue>
#include<stack>
#include<vector>
#define tree int o,int l,int r
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define lo o<<1
#define ro o<<1|1
#define pb push_back
#define mp make_pair
#define ULL unsigned long long
#define LL long long
#define inf 0x7fffffff
#define eps 1e-7
#define N 50009
using namespace std;
int m,n,T,t,x,y,z;
vector<pair<int,int> >g[N];
int f[N],s[N],sub[N];
int qs[N],qj[N];
void init()
{
    for (int i=0; i<=n; ++i )
        g[i].clear();
}
void dfs1(int u,int fa)
{
    f[u]=s[u]=0;
    for(int i=0; i<g[u].size(); i++)
    {
        int v=g[u][i].first;
        int l=g[u][i].second;
        if(v!=fa)
        {
            dfs1(v,u);
            if(f[u]<f[v]+l)
            {
                s[u]=f[v]+l;
                swap(s[u],f[u]);
                sub[u]=v;
            }
            else if(s[u]<f[v]+l)
            {
                s[u]=f[v]+l;
            }
        }
    }
}
void dfs2(int u,int fa)
{
    for(int i=0; i<g[u].size(); i++)
    {
        int v=g[u][i].first;
        int l=g[u][i].second;
        if(v!=fa)
        {
            int val;
            if(sub[u]==v)
                val=s[u];
            else
                val=f[u];
            val+=l;
            if(f[v]<val)
            {
                s[v]=val;
                swap(s[v],f[v]);
                sub[v]=u;
            }
            else if(s[v]<val)
            {
                s[v]=val;
            }
            dfs2(v,u);
        }
    }
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("ex.in","r",stdin);
#endif
    while(scanf("%d%d%*c",&n,&m)==2)
    {
        if(!n&&!m)return 0;
        init();
        for (int i=0; i<n-1; ++i )
        {
            scanf("%d%d%d",&x,&y,&z);
            g[x].pb(mp(y,z));
            g[y].pb(mp(x,z));
        }
        dfs1(1,-1);
        dfs2(1,-1);
        while(m--)//O(mn)
        {
            int q,ans=1;
            scanf("%d",&q);
            int sl=0,sr=0,jl=0,jr=0,temp=0;
            for(int i=1; i<=n; i++)
            {
                while(sl<sr&&f[qs[sr-1]]>=f[i])sr--;
                while(jl<jr&&f[qj[jr-1]]<=f[i])jr--;
                qs[sr++]=i;
                qj[jr++]=i;
                while(f[qj[jl]]-f[qs[sl]]>q)//对于右区间i,找到左区间!
                {
                    if(qj[jl]<qs[sl])
                        temp=max(temp,qj[jl++]);
                    else
                        temp=max(temp,qs[sl++]);
                }
                ans=max(ans,i-temp);
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}
View Code

 

 

posted @ 2013-10-23 19:16  baoff  阅读(1406)  评论(0编辑  收藏  举报