掉坑记录--two pointer


/*--------------------------------------------
two pointer 失败案例! 
我们知道,two pointer它实际上,可以快速的求到
一串数字中任意两数之和的各种统计类问题。但是
这种统计具有局限性,即它不能附带任何附加条件。

比如说,这个程序,要求求满足两点间距离小于E且
大于S的这个距离的最短值。
我使用的算法是退化版的点分.我在处理每个子树的
时候用到了two pointer,希望用它来计算点u 的子
树中任意两点经过u的路径长度,但是实际上为了避
免v->u->v这种尴尬路径的存在,我使用了并查集判
掉了lf和rg属于同一棵子树的情况,并直接使rg--继
续判断。

但是这样是不行的,因为lf->u->(rg-1)的路径可能
并不比(lf+1)->u->rg优,这样的话,一旦我们走错
正确答案就没法更新到ans上,就会导致错误

因此,这个程序在某些数据下会出错。解决办法是,
在每个子树dfs完之后,sort一遍,并把u下已经dfs
过的其他也sort一遍,令lf在其他部分,rg在当前子
树部分,进行two pointer求和操作。这样就消除了
原本存在的附加条件。不过.这样带来的时间复杂度
也是十分庞大的,因为每个子树需要sort两遍,因此
总复杂度翻倍。可以适当优化常数 

--------------------------------------------*/

#include <ctime>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std ;

struct Path{
    int pre , to , len ;
}p[400005];
struct Data{
    int id;long long  dep ;
    inline bool operator < (const Data &A ) const{
        return dep < A.dep ;
    }
}d[200005];
int N ,m, S , E , head[200005] , tp , fa[200005] , fffa[100005] ;
long long ans = 21474836464994545LL;

inline void In( int t1 , int  t2 , int t3 ){
    p[++tp].pre = head[t1] ;
    p[tp].to = t2 ;
    p[tp].len = t3 ;
    head[t1] = tp ;
}

inline int find( int x ){
    return fa[x] == x ? x : fa[x] = find( fa[x] ) ;
}
int root,sz[200005],mx[200005]={2147483646};
inline void dfsroot(int u,int fa){
    sz[u]=1;
    for(register int v,i=head[u];i;i=p[i].pre){
        if(v=p[i].to,v!=fa){
            dfsroot(v,u),sz[u]+=sz[v];
            mx[u]=max(sz[v],mx[u]);
        }
    }
    mx[u]=max(mx[u],m-sz[u]);
    if(mx[u]<mx[root])root=u;
}
int in[200005] , out[200005] , dfs_c ;
inline void dfs( int u , int f ){
    fffa[u] = f ;
    in[u] = ++dfs_c ;
    d[in[u]].id = u ;
    long long nowd = d[in[u]].dep ;
    for(register int i = head[u] ; i ; i = p[i].pre ){
        int v = p[i].to ;
        if( v != f ){
            d[dfs_c+1].dep = nowd + p[i].len ;
            dfs( v , u ) ;
        }
    }
    out[u] = dfs_c ;


    int lf = in[u] , rg = out[u] ;
    sort( d+lf+1 , d+rg+1 ) ;
    while( lf != rg ){
        long long len = d[lf].dep + d[rg].dep - 2*nowd ;
        if( len < S )
            lf ++ ;
        else if( len > E )
            rg -- ;
        else{
            if(  find(d[lf].id) != find(d[rg].id) )
                ans = min(ans  , len ) ;
            rg -- ;
        }
    }


    for(register int i = head[u] ; i ; i = p[i].pre ){
        int v = p[i].to ;
        if( v != f ) fa[v] = u;
    }
}

template<class T>inline void read(T &res){
    static char ch;T flag=1;
    while((ch=getchar())<'0'||ch>'9')if(ch=='-')flag=-1;res=ch-48;
    while((ch=getchar())>='0'&&ch<='9')res=res*10+ch-48;res*=flag;
}

int main(){
    freopen( "B7.in" , "r" , stdin ) ;
    //freopen( "path.out", "w" , stdout) ;
    int t1 , t2 , t3 ;
    scanf( "%d%d%d" , &N , &S , &E ) ;

    for(register int i = 1 ; i < N ; ++i ){
        read(t1) , read(t2) , read(t3) ;
        In( t1 , t2 , t3 ) ;In( t2 , t1 , t3 ) ;
        fa[i] = i ;
    }
    fa[N] = N ;
    m=N,dfsroot(1,0);
    dfs(root, 0 ) ;
    //dfs(N/2+1,0); 
    printf("%I64d",(ans == 21474836464994545LL ?

修改之后的代码:

其实这个方法是过不了链状数据和菊花图数据的
只是因为这边数据是随机的,反而正规的二分check+点分还要快

#include <ctime>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std ;

struct Path{
    int pre , to , len ;
}p[400005];
struct Data{
    int id , dep ;
    inline bool operator < (const Data &A ) const{
        return dep < A.dep ;
    }
}d[200005];
int N ,m, S , E , head[200005] , tp , fa[200005] , ans = 2147483646 ;

inline void In( int t1 , int  t2 , int t3 ){
    p[++tp].pre = head[t1] ;
    p[tp].to = t2 ;
    p[tp].len = t3 ;
    head[t1] = tp ;
}

int in[200005] , out[200005] , dfs_c ;
inline void dfs( int u , int f ){
    in[u] = ++dfs_c ;
    d[in[u]].id = u ;
    int nowd = d[in[u]].dep ;
    for(register int i = head[u] ; i ; i = p[i].pre ){
        int v = p[i].to ;
        if( v != f ){
            d[dfs_c+1].dep = nowd + p[i].len ;
            dfs( v , u ) ;
            sort( d+in[u]+1 , d+in[v] ) ;
            sort( d+in[v]+1 , d+out[v]+1 ) ;

            int lf = in[u] , rg = out[v] ;
            while( lf < in[v] && rg >= in[v] ){
                int len = d[lf].dep + d[rg].dep - 2 * nowd ;
                if( len < S ) lf ++ ;
                else{
                    ans = min( ans , len ) ;
                    rg -- ;
                }
            }

        }
    }
    out[u] = dfs_c ;
}

template<class T>inline void read(T &res){
    static char ch;T flag=1;
    while((ch=getchar())<'0'||ch>'9')if(ch=='-')flag=-1;res=ch-48;
    while((ch=getchar())>='0'&&ch<='9')res=res*10+ch-48;res*=flag;
}

int main(){
    freopen( "path.in" , "r" , stdin ) ;
    freopen( "path.out", "w" , stdout) ;
    int t1 , t2 , t3 ;
    scanf( "%d%d%d" , &N , &S , &E ) ;

    for(register int i = 1 ; i < N ; ++i ){
        read(t1) , read(t2) , read(t3) ;
        In( t1 , t2 , t3 ) ;In( t2 , t1 , t3 ) ;
    }
    dfs(N/2+1,0);
    printf("%d",(ans > E ? -1 : ans ) );
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值