参考博客:

https://www.cnblogs.com/ice-wing/p/7709311.html

https://www.luogu.org/blog/sincereactor/shu-shang-ci-fen-di-liang-zhong-sai-lu

https://blog.csdn.net/Fine_rose/article/details/77991839

https://blog.csdn.net/ArliaStark/article/details/80720181

已知路径求被所有路径覆盖的边

首先对已知的这 n 条路径的起点a和终点b的权值+1,并对lca(a,b)的权值-2 。

从根节点开始深搜,回溯时将其本身的权值加上所有子节点的权值。

那么满足要求的边就是权值等于n的节点与其父节点所连的边。

例题:

P2680

询问m条链中,删去一条边,使权值最大的链权值最小。

思路:

二分最大链的权值L。

对于链长小于等于L的,不用管。

对于链长大于L的,要确保删的这条边在该链上。

所以选定的这条边要被所有链长大于L的链所覆盖,在符合条件的边里选最长的那一条才最优,然后判断当前最长链-该边的权值是否小于这个二分的最大链的权值即可。

代码:

struct edge
{
    int to;
    int cost;
    int next;
}eg[300010*2];
struct imfo
{
    int a,b,lc;
    int len;
}p[300010];
int val[300010];//i->par[i]的权值
int top;
int head[300010];
int tot[300010];
int son[300010];
int idx[300010];
int topp[300010];
int par[300010];
int dep[300010];
int dis[300010];
int n,m;
int mark[300010];
int pre[300010];
int maxlen;
int cnt;
inline int read()
{
    char c=getchar();
    int x=0;
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
    return x;
}
void add(int a,int b,int t)
{
    eg[top].to=b;
    eg[top].cost=t;
    eg[top].next=head[a];
    head[a]=top++;
}
int dfs1(int id,int fa,int depth,int sum)
{
    dep[id]=depth;
    tot[id]=1;
    dis[id]=sum;
    par[id]=fa;
    int maxson=-1;
    son[id]=0;
    for(int i=head[id];i!=-1;i=eg[i].next)
    {
        int temp=eg[i].to;
        if(temp==fa)continue;
        val[eg[i].to]=eg[i].cost;
        tot[id]+=dfs1(eg[i].to,id,depth+1,sum+val[eg[i].to]);
        if(tot[temp]>maxson)
        {
            son[id]=temp;
            maxson=tot[temp];
        }
    }
    return tot[id];
}
void dfs2(int id,int topf)
{
    idx[id]=++cnt;
    topp[id]=topf;
    if(son[id]==0)return;     //子节点的son为0
    dfs2(son[id],topf);     //优先标记重儿子
    for(int i=head[id];i!=-1;i=eg[i].next)
    {
        int temp=eg[i].to;
        if(idx[temp]==0)
            dfs2(temp,temp);     //每一条链以轻儿子为起点
    }
}
int lca(int x,int y)
{
    while(topp[x]!=topp[y])
    {
        if(dep[topp[x]]<dep[topp[y]])swap(x,y);     //谁的top低谁就往上走
        x=par[topp[x]];
    }
    if(dep[x]>dep[y])swap(x,y);
    return x;
}
int calcpre(int id,int fa)
{
    pre[id]=0;
    for(int i=head[id];i!=-1;i=eg[i].next)
        if(eg[i].to!=fa)
            pre[id]+=calcpre(eg[i].to,id);
    pre[id]+=mark[id];
    return pre[id];
}
bool judge(int x)
{
    int num=0;
    for(int i=1;i<=m;i++)
    {
        if(p[i].len>x)
        {
            mark[p[i].a]++;
            mark[p[i].b]++;
            mark[p[i].lc]-=2;
            num++;
        }
    }
    if(num==0)return true;
    calcpre(1,1);
    int MAX=-1;
    for(int i=1;i<=n;i++)
    {
        if(pre[i]==num)
            MAX=max(MAX,val[i]);
        mark[i]=0;
    }
    if(maxlen-MAX<=x)return true;
    return false;
}
int main()
{
    int a,b,t;
    n=read();m=read();
    top=0;
    memset(head,-1,sizeof(head));
    for(int i=0;i<n-1;i++)
    {
        a=read();b=read();t=read();
        add(a,b,t);add(b,a,t);
    }
    cnt=0;
    dfs1(1,1,1,0);
    dfs2(1,1);
    maxlen=-1;
    for(int i=1;i<=m;i++)
    {
        p[i].a=read();p[i].b=read();
        p[i].lc=lca(p[i].a,p[i].b);
        p[i].len=dis[p[i].a]+dis[p[i].b]-2*dis[p[i].lc];
        maxlen=max(maxlen,p[i].len);
    }
    int l=0,r=maxlen,ans=0;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(judge(mid))ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%d\n",ans);
    return 0;
}

已知路径求树上所有节点被路径覆盖次数

对每条路径的起点a和终点b的权值+1 , 对lca(a, b)的权值-1 , 对lca(a,b)的父节点权值-1。

从根节点开始深搜,回溯时将其本身的权值加上所有子节点的权值。

每个节点的权值即是其被路径覆盖的次数。

例题:

P3258

给出一棵树,以及访问结点的顺序,问每个结点要访问几次,访问最后一个结点不算。

思路:裸题。

代码:

int a[300010];
vector<int>v[300010];
int par[300010];
int dep[300010];
int tot[300010];
int son[300010];
int top[300010];
int idx[300010];
int mark[300010];
int ans[300010];
int cnt;
int dfs1(int id,int fa,int depth)
{
    par[id]=fa;
    dep[id]=depth;
    int maxson=-1;
    son[id]=0;
    int len=v[id].size();
    for(int i=0;i<len;i++)
    {
        int temp=v[id][i];
        if(temp==fa)continue;
        tot[id]+=dfs1(temp,id,depth+1);
        if(tot[temp]>maxson)
        {
            son[id]=temp;
            maxson=tot[temp];
        }
    }
    return tot[id];
}
void dfs2(int id,int topf)
{
    idx[id]=++cnt;
    top[id]=topf;
    if(son[id]==0)return;
    dfs2(son[id],topf);
    int len=v[id].size();
    for(int i=0;i<len;i++)
    {
        int temp=v[id][i];
        if(idx[temp]==0)dfs2(temp,temp);
    }
}
int lca(int x,int y)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        x=par[top[x]];
    }
    if(dep[x]>dep[y])swap(x,y);
    return x;
}
int dfs(int id,int fa)
{
    int len=v[id].size();
    ans[id]=mark[id];
    for(int i=0;i<len;i++)
    {
        int temp=v[id][i];
        if(temp==fa)continue;
        ans[id]+=dfs(temp,id);
    }
    return ans[id];
}
int main()
{
    int n,x,y;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    memset(idx,0,sizeof(idx));
    for(int i=1;i<=n-1;i++)
    {
        scanf("%d%d",&x,&y);
        v[x].push_back(y);
        v[y].push_back(x);
    }
    dfs1(1,0,1);
    cnt=0;
    dfs2(1,0);
    for(int i=1;i<n;i++)
    {
        mark[a[i]]++;
        mark[a[i+1]]++;
        int lc=lca(a[i],a[i+1]);
        mark[lc]--;
        mark[par[lc]]--;
    }
    dfs(1,0);
    for(int i=1;i<=n;i++)
    {
        if(i==a[1])printf("%d\n",ans[i]);
        else printf("%d\n",ans[i]-1);
    }
    return 0;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

GCPC2014C 双调欧几里得旅行商问题 Previous
概率 & 期望 Next