zoukankan      html  css  js  c++  java
  • 0x63树的直径与最近公共祖先

    bzoj1999 先把树的直径求出来,从左往右枚举,对于当前位置i,找到满足限制并且最远的点j,当前位置最大值就是max(i~j区间内除直径外的子树路径长度最大值,1~i的长度,j~n的长度)

    然而,对于树的直径有一个很有用的性质,1~i区间内除直径外的子树路径长度最大值必然不会比1~i的长度大,否则就不是直径了。j~n同理

    所以可以把i~j区间转换成1~n区间,成为定值。求完树的直径后O(n)即可出解

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    
    int n,S;
    struct node
    {
        int x,y,d,next;
    }a[1100000];int len,last[510000];
    void ins(int x,int y,int d)
    {
        len++;
        a[len].x=x;a[len].y=y;a[len].d=d;
        a[len].next=last[x];last[x]=len;
    }
    int L,ld,R,rd;
    void getL(int x,int fr,int d)
    {
        if(L==0||d>ld)L=x,ld=d;
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(y!=fr)
                getL(y,x,d+a[k].d);
        }
    }
    int pre[510000];
    void getR(int x,int fr,int d)
    {
        if(R==0||d>rd)R=x,rd=d;
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(y!=fr)
            {
                pre[y]=k;
                getR(y,x,d+a[k].d);
            }
        }
    }
    bool v[510000];
    int getmax(int x,int fr)
    {
        int mx=0;
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(v[y]==false&&y!=fr)
                mx=max(mx,getmax(y,x)+a[k].d);
        }
        return mx;
    }
    
    int ulen,u[510000],dis[510000];
    int main()
    {
    //    freopen("core.in","r",stdin);
    //    freopen("core.out","w",stdout);
        int x,y,dd;
        scanf("%d%d",&n,&S);
        len=0;memset(last,0,sizeof(last));
        for(int i=1;i<n;i++)
        {
            scanf("%d%d%d",&x,&y,&dd);
            ins(x,y,dd);ins(y,x,dd);
        }
        L=0;getL(1,0,0);
        R=0;getR(L,0,0);
        
        memset(v,false,sizeof(v));
        ulen=0;u[++ulen]=R;dis[ulen]=0;v[R]=true;
        int k=pre[R];
        while(a[k].x!=L)
        {
            u[++ulen]=a[k].x;
            dis[ulen]=a[k].d+dis[ulen-1];
            v[a[k].x]=true;
            k=pre[a[k].x];
        }
        u[++ulen]=a[k].x;
        dis[ulen]=a[k].d+dis[ulen-1];
        v[a[k].x]=true;
        
        int mx=0,j=1;
        for(int i=1;i<=ulen;i++)mx=max(mx,getmax(u[i],0));
        int mn=2147483647;
        for(int i=1;i<=ulen;i++)
        {
            while(j<ulen&&dis[j+1]-dis[i]<=S)j++;
            mn=min(mn,max(mx,max(dis[i],dis[ulen]-dis[j])));
        }
        printf("%d
    ",mn);
        return 0;
    }
    bzoj1999

    poj3417 环的思想很重要。对于一条非树边,可以理解成和树上路径构成一个环,若删掉一条树边,删掉当前这条非树边,树边能够选择的就是在x,y树上最短路的边。树上路径修改,采用树上差分的做法。此后,遍历每一条边,计算答案。对于只被1个非树边覆盖的树边,ans++,只被0个覆盖的,所有非树边都可以随意。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    
    struct node
    {
        int x,y,next;
    }a[210000];int len,last[110000];
    void ins(int x,int y)
    {
        len++;
        a[len].x=x;a[len].y=y;
        a[len].next=last[x];last[x]=len;
    }
    int f[25][110000],dep[110000],Bin[30];
    void dfs(int x)
    {
        for(int i=1;dep[x]>=Bin[i];i++)f[i][x]=f[i-1][f[i-1][x]];
        
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(y!=f[0][x])
            {
                f[0][y]=x;
                dep[y]=dep[x]+1;
                dfs(y);
            }
        }
    }
    int LCA(int x,int y)
    {
        if(dep[x]<dep[y])swap(x,y);
        for(int i=22;i>=0;i--)
            if(dep[x]-dep[y]>=Bin[i])x=f[i][x];
        if(x==y)return x;
        for(int i=22;i>=0;i--)
            if(dep[x]>=Bin[i]&&f[i][x]!=f[i][y])x=f[i][x],y=f[i][y];
        return f[0][x];
    }
    
    int ans,m,d[110000];
    void getans(int x)
    {
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(y!=f[0][x])
                getans(y), d[x]+=d[y];
        }
        if(x!=1)
        {
            if(d[x]==0)ans+=m;
            else if(d[x]==1)ans++;
        }
    }
    int main()
    {
        Bin[0]=1;for(int i=1;i<=22;i++)Bin[i]=Bin[i-1]*2;
        int n,x,y;
        scanf("%d%d",&n,&m);
        len=0;memset(last,0,sizeof(last));
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            ins(x,y);ins(y,x);
        }
        dfs(1);
        
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            d[x]++;d[y]++;
            d[LCA(x,y)]-=2;
        }
        
        ans=0;
        getans(1);
        printf("%d
    ",ans);
        
        return 0;
    }
    poj3417

    CH Round #56-C 对于当前出现的异象石,按照dfs序排序,对于相邻的点求距离(包括首尾),答案就是总距离/2。动态维护这个序列,就是平衡树啊。操作简单,什么set啊,或者线段树啊奇淫维护就好

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    #include<set>
    using namespace std;
    typedef long long LL;
    
    struct node
    {
        int x,y,next;LL d;
    }a[210000];int len,last[110000];
    void ins(int x,int y,LL d)
    {
        len++;
        a[len].x=x;a[len].y=y;a[len].d=d;
        a[len].next=last[x];last[x]=len;
    }
    int Bin[25];
    int f[25][110000],dep[110000];
    int z,ys[110000];LL d[110000];
    void dfs(int x)
    {
        for(int i=1;dep[x]>=Bin[i];i++)f[i][x]=f[i-1][f[i-1][x]];
        ys[x]=++z;
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(y!=f[0][x])
            {
                f[0][y]=x;
                dep[y]=dep[x]+1;
                d[y]=d[x]+a[k].d;
                dfs(y);
            }
        }
    }
    int LCA(int x,int y)
    {
        if(dep[x]<dep[y])swap(x,y);
        for(int i=22;i>=0;i--)
            if(dep[x]-dep[y]>=Bin[i])x=f[i][x];
        if(x==y)return x;
        for(int i=22;i>=0;i--)
            if(dep[x]>=Bin[i]&&f[i][x]!=f[i][y])x=f[i][x],y=f[i][y];
        return f[0][x];
    }
    LL getdis(int x,int y){return d[x]+d[y]-d[LCA(x,y)]*2;}
    
    struct myset
    {
        int t,id;
        myset(){}
        myset(int T,int ID){t=T;id=ID;}
        friend bool operator>(myset s1,myset s2){return s1.t>s2.t;}
        friend bool operator<(myset s1,myset s2){return s1.t<s2.t;}
    }L,R,mL,mR;
    set<myset>s;
    int main()
    {
        Bin[0]=1;for(int i=1;i<=22;i++)Bin[i]=Bin[i-1]*2;
        int n,x,y;LL dd;
        scanf("%d",&n);
        len=0;memset(last,0,sizeof(last));
        for(int i=1;i<n;i++)
        {
            scanf("%d%d%lld",&x,&y,&dd);
            ins(x,y,dd);ins(y,x,dd);
        }
        f[0][1]=0;z=0;d[1]=0;
        dfs(1);
        
        int Q;LL ans=0; char ss[10];
        scanf("%d",&Q);
        while(Q--)
        {
            scanf("%s",ss+1);
            if(ss[1]=='?')printf("%lld
    ",ans/2);
            else if(ss[1]=='+')
            {
                scanf("%d",&x);
                if(!s.empty())
                {
                    mL=*s.begin();mR=*--s.end();
                    
                    if(ys[x]<mL.t)L=mR;
                    else L=*--s.lower_bound(myset(ys[x],x));
                    
                    if(mR.t<ys[x])R=mL;
                    else R=*s.lower_bound(myset(ys[x],x));
                    
                    ans-=getdis(L.id,R.id);
                    ans+=getdis(L.id,x);
                    ans+=getdis(x,R.id);
                }
                s.insert(myset(ys[x],x));
            }
            else
            {
                scanf("%d",&x);
                s.erase(myset(ys[x],x));
                if(!s.empty())
                {
                    mL=*s.begin();mR=*--s.end();
                    
                    if(ys[x]<mL.t)L=mR;
                    else L=*--s.lower_bound(myset(ys[x],x));
                    
                    if(mR.t<ys[x])R=mL;
                    else R=*s.lower_bound(myset(ys[x],x));
                    
                    ans+=getdis(L.id,R.id);
                    ans-=getdis(L.id,x);
                    ans-=getdis(x,R.id);
                }
            }
        }
        return 0;
    }
    异象石

    bzoj1977 同样是环的思想。先求出一棵最小生成树,对于非树边,必然是从和树边构成的环中,断掉最大的。然而题目要求严格次小生成树,当前非树边边权可能和最大边权相等,因此需要多维护一个严格次大值。这个和倍增求最近公共祖先的原理是一样的。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long LL;
    
    struct node
    {
        int x,y,next;LL d;
    }a[210000];int len,last[110000];
    void ins(int x,int y,LL d)
    {
        len++;
        a[len].x=x;a[len].y=y;a[len].d=d;
        a[len].next=last[x];last[x]=len;
    }
    int Bin[30];
    int f[25][110000],dep[110000];
    LL mx[25][110000],sx[25][110000];
    LL smx(LL m1,LL s1,LL m2,LL s2)
    {
        LL ret=max(s1,s2);
        if(m1!=m2)ret=max(ret,min(m1,m2));
        return ret;
    }
    void dfs(int x)
    {
        for(int i=1;Bin[i]<dep[x];i++)
        {
            f[i][x]=f[i-1][f[i-1][x]];
            
            int m1=mx[i-1][x],m2=mx[i-1][f[i-1][x]];
            int s1=sx[i-1][x],s2=sx[i-1][f[i-1][x]];
            
            mx[i][x]=max(m1,m2);
            sx[i][x]=smx(m1,s1,m2,s2);
        }
        
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(f[0][x]!=y)
            {
                f[0][y]=x;
                mx[0][y]=a[k].d;
                dep[y]=dep[x]+1;
                dfs(y);
            }
        }
    }
    void getmax(int x,int y,LL &mmax,LL &smax)
    {
        if(dep[y]>dep[x])swap(x,y);
        
        for(int i=22;i>=0;i--)
            if(dep[x]-dep[y]>=Bin[i])
            {
                smax=smx(mmax,smax,mx[i][x],sx[i][x]);
                mmax=max(mmax,mx[i][x]);
                x=f[i][x];
            }
            
        for(int i=22;i>=0;i--)
            if(dep[x]>Bin[i]&&f[i][x]!=f[i][y])
            {
                smax=smx(mmax,smax,mx[i][x],sx[i][x]);
                smax=smx(mmax,smax,mx[i][y],sx[i][y]);
                mmax=max(mmax,max(mx[i][x],mx[i][y]));
                x=f[i][x],y=f[i][y];
            }
        smax=smx(mmax,smax,mx[0][x],sx[0][x]);
        smax=smx(mmax,smax,mx[0][y],sx[0][y]);
        mmax=max(mmax,max(mx[0][x],mx[0][y]));
    }
    
    struct edge{int x,y;LL d;}e[310000],lz[310000];
    bool cmp(edge e1,edge e2){return e1.d<e2.d;}
    int fa[110000];
    int findfa(int x)
    {
        if(x==fa[x])return x;
        fa[x]=findfa(fa[x]);return fa[x];
    }
    
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
            scanf("%d%d%lld",&e[i].x,&e[i].y,&e[i].d);
        sort(e+1,e+m+1,cmp);
        
        for(int i=1;i<=n;i++)fa[i]=i;
        int tp=0;LL mind=0;
        for(int i=1;i<=m;i++)
        {
            int fx=findfa(e[i].x),fy=findfa(e[i].y);
            if(fx!=fy)
            {
                fa[fx]=fy;mind+=e[i].d;
                ins(e[i].x,e[i].y,e[i].d);
                ins(e[i].y,e[i].x,e[i].d);
            }
            else lz[++tp]=e[i];
        }
        m=tp;
        
        f[0][1]=0;
        memset(sx,-1,sizeof(sx));
        Bin[0]=1;for(int i=1;i<=22;i++)Bin[i]=Bin[i-1]*2;
        dep[1]=1;dfs(1);
        
        LL ans=(1LL<<62);
        for(int i=1;i<=m;i++)
        {
            LL mmax=-(1LL<<62),smax=-(1LL<<62); getmax(lz[i].x,lz[i].y,mmax,smax);
            if(mmax==lz[i].d)
            {
                if(smax!=-1)
                    ans=min(ans,mind-smax+lz[i].d);
            }
            else ans=min(ans,mind-mmax+lz[i].d);
        }
        printf("%lld
    ",ans);
        return 0;
    }
    bzoj1977

    NOIP2009 D2T3

    容易想到二分答案。对于军队而言,必然是越向上控制的叶子节点越多,那么尽量向上。先不管根,假如军队无法到达根的子节点,那么根据前面的贪心位置确定。否则,有两种情况:1、留在当前子节点,2、去往根的另一个孩子。

    此时,题目就转化成:给你n个点,m个军队,军队有处在的点,有权值,点亦有权。军队可以占领自己的位置,或者占领点权+自己处于的点的点权<=自身权值的位置。

    瓶颈就在于我可以留在当前的点,没有花费,特殊性造成的影响就是可能一个军队可能从一个点出发,却又回到这个点,相当于点权耗费两次,判断是否能够到达自己会出错。这样一来就无法把军队集中,统一贪心地分配。

    如何去掉这个限制?对于一个军队,假如它的权值<=所处点权*2,必然不会去其他点。因为去,也只能去比当前点权值小的点,然而你还需要另一支军队占领当前点,既然这支军队可以占领当前点,那么也一定可以占领前一支军队占领的点。

    处理完这个以后,我们就能够保证,即使点权耗费两次,而当前点的军队的权都是>二倍点权的,就不会出现不能到达自己的情况。由此就可以忽略第1个限制了。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long LL;
    
    struct node
    {
        int x,y,next;LL d;
    }a[110000];int len,last[51000];
    void ins(int x,int y,LL d)
    {
        len++;
        a[len].x=x;a[len].y=y;a[len].d=d;
        a[len].next=last[x];last[x]=len;
    }
    int Bin[25];LL d[25][51000];
    int f[25][51000],dep[51000],son[51000];
    void dfs(int x)
    {
        for(int i=1;Bin[i]<dep[x];i++)
        {
            d[i][x]=d[i-1][x]+d[i-1][f[i-1][x]];
            f[i][x]=f[i-1][f[i-1][x]];
        }
        
        son[x]=0;
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(y!=f[0][x])
            {
                son[x]++;
                dep[y]=dep[x]+1;
                f[0][y]=x;
                d[0][y]=a[k].d;
                dfs(y);
            }
        }
    }
    
    int m,p[51000],s[51000];bool v[51000],b[51000];
    struct stack{LL d;int id;}sta[51000];int top;
    int arlen,ctlen;LL ar[51000],ct[51000];
    bool check(LL mid)
    {
        memset(v,false,sizeof(v));
        for(int k=last[1];k;k=a[k].next)b[a[k].y]=false;
        memcpy(s,son,sizeof(s));
        top=0;
        for(int i=1;i<=m;i++)
        {
            int x=p[i];LL w=mid;
            for(int j=22;j>=0;j--)
                if(dep[x]>Bin[j]&&w>=d[j][x]&&f[j][x]!=1)w-=d[j][x],x=f[j][x];
                
            if(f[0][x]!=1)
            {
                bool bk=false;
                for(int k=x;k!=1;k=f[0][k])if(v[k]==true){bk=true;break;}
                if(bk==false)
                {
                    v[x]=true;
                    s[f[0][x]]--;
                    while(s[f[0][x]]==0)
                    {
                        v[f[0][x]]=true;
                        x=f[0][x],s[f[0][x]]--;
                        if(f[0][x]==1){b[x]=true;break;}
                    }
                }
            }
            else sta[++top].d=w,sta[top].id=x;
        }
        
        arlen=0;
        for(int i=1;i<=top;i++)
            if(b[sta[i].id]==false)
            {
                if(d[0][sta[i].id]*2>=sta[i].d)b[sta[i].id]=true;
                else ar[++arlen]=sta[i].d-d[0][sta[i].id];
            }
            else ar[++arlen]=sta[i].d-d[0][sta[i].id];
        sort(ar+1,ar+arlen+1);
        
        ctlen=0;
        for(int k=last[1];k;k=a[k].next)
            if(b[a[k].y]==false)ct[++ctlen]=a[k].d;
        sort(ct+1,ct+ctlen+1);
        
        int j=1;
        for(int i=1;i<=ctlen;i++)
        {
            while(j<arlen&&ar[j]<ct[i])j++;
            if(ar[j]<ct[i])return false;
            j++;
            if(j>arlen&&i<ctlen)return false;
        }
        return true;
    }
    int main()
    {
        int n,x,y;LL dd;
        scanf("%d",&n);
        for(int i=1;i<n;i++)
        {
            scanf("%d%d%lld",&x,&y,&dd);
            ins(x,y,dd);ins(y,x,dd);
        }
        Bin[0]=1;for(int i=1;i<=22;i++)Bin[i]=Bin[i-1]*2;
        f[0][1]=0;dep[1]=1;dfs(1);
        
        scanf("%d",&m); if(son[x]>m){printf("-1
    ");return 0;}
        for(int i=1;i<=m;i++)scanf("%d",&p[i]);
        
        LL l=1,r=5*1e13,ans;
        while(l<=r)
        {
            LL mid=(l+r)/2;
            if(check(mid))
            {
                ans=mid;
                r=mid-1;
            }
            else l=mid+1;
        }
        printf("%lld
    ",ans);
        return 0;
    }
    疫情控制
  • 相关阅读:
    [YTU]_2637(编程题:类---矩形类)
    [YTU]_2625( 构造函数和析构函数)
    [YTU]_2498 (C++类实现最大数的输出)
    [YTU]_2433( C++习题 对象数组求最大值)
    [YTU]_2432( C++习题 对象数组输入与输出)
    AC自动机模板1
    trie字典树
    KMP模板
    Count(广工14届竞赛)
    zyb的面试(广工14届比赛)
  • 原文地址:https://www.cnblogs.com/AKCqhzdy/p/9536546.html
Copyright © 2011-2022 走看看