CF1707C - DFS Trees (树上差分)

题意:每次优先按权重最小的边dfs,不重复搜一个节点,问从那些点作为根节点搜索得到的路径与最小生成树一样?

Problem - C - Codeforces

思路见官方题解 Codeforces Round #808 Editorial - Codeforces

主要是先得到最小生成树,每条多余的边能排除一些最小生成树的点.比如上图多余的边是(u,v),就能把u,v以下的点都排除了.

但是怎么在O(n)复杂度得到这些点?,就要用到差分思想,参考树上差分详解 - TEoS - 博客园

差分思想就是用一些特殊点代表一种累计效果.最后再求一次前缀和. 不同情况下细节略有不同.

 改题中还需要求lca

import java.util.*;
import java.io.*;

public class Main {
    public static void main(String args[]) {new Main().run();}

    FastReader in = new FastReader();
    PrintWriter out = new PrintWriter(System.out);
    void run() {
        work();
        out.flush();
    }
    long mod=998244353;
    long gcd(long a,long b) {
        return a==0?b:gcd(b%a,a);
    }
    final long inf=Long.MAX_VALUE/3;
    ArrayList<Integer>[] graph;
    int[] id;
    int[][] dp;
    int[] deep;
    int[] rec;
    void work(){
        int n=ni(),m=ni();
        graph=new ArrayList[n];
        deep=new int[n];
        dp=new int[n][20];
        id=new int[n];
        for(int i=0;i<n;i++){
            graph[i]=new ArrayList<>();
            id[i]=i;
        }
        rec=new int[n];//差分
        int[][] edge=new int[m-n+1][];
        for(int i=0,j=0;i<m;i++){
            int s=ni()-1,e=ni()-1;
            if(union(s,e)){
                edge[j++]=new int[]{s,e};
            }else{
                graph[s].add(e);
                graph[e].add(s);
            }
        }
        dfs(0,-1);
        for(int i=0;i<edge.length;i++){
            int n1=edge[i][0],n2=edge[i][1];
            int lca = lca(n1, n2);
            if(lca==n1){
                int cur=n2;
                for(int j=19;j>=0;j--){
                    if(dp[cur][j]!=-1&&deep[dp[cur][j]]>deep[n1]){
                        cur=dp[cur][j];
                    }
                }
                rec[cur]++;
                rec[n2]--;
            }else if(lca==n2){
                int cur=n1;
                for(int j=19;j>=0;j--){
                    if(dp[cur][j]!=-1&&deep[dp[cur][j]]>deep[n2]){
                        cur=dp[cur][j];
                    }
                }
                rec[cur]++;
                rec[n1]--;
            }else{
                rec[0]++;
                rec[n1]--;
                rec[n2]--;
            }
        }
        dfs2(0,-1);
        StringBuilder sb=new StringBuilder();
        for(int i=0;i<n;i++){
            if(rec[i]>0)sb.append(0);
            else sb.append(1);
        }
        out.println(sb.toString());
    }

    private void dfs2(int node, int pre) {
        if(pre!=-1){
            rec[node]+=rec[pre];
        }
        for(int nn:graph[node]){
            if(nn!=pre){
                dfs2(nn,node);
            }
        }
    }

    private void dfs(int node, int pre) {
        dp[node][0]=pre;
        if(pre!=-1){
            deep[node]=deep[pre]+1;
        }
        for(int i=1;i<20&&dp[node][i-1]!=-1;i++){
            dp[node][i]=dp[dp[node][i-1]][i-1];
        }
        for(int nn:graph[node]){
            if(nn!=pre){
                dfs(nn,node);
            }
        }
    }

    private int lca(int n1,int n2){
        int d1=deep[n1],d2=deep[n2];
        int d=Math.min(d1,d2);
        int cur1=n1,cur2=n2;
        for(int i=19;i>=0;i--){
            if(dp[cur1][i]!=-1&&deep[dp[cur1][i]]>=d){
                cur1=dp[cur1][i];
            }
        }
        for(int i=19;i>=0;i--){
            if(dp[cur2][i]!=-1&&deep[dp[cur2][i]]>=d){
                cur2=dp[cur2][i];
            }
        }
        if(cur1==cur2)return cur1;
        for(int i=19;i>=0;i--){
            if(dp[cur1][i]!=dp[cur2][i]){
                cur1=dp[cur1][i];
                cur2=dp[cur2][i];
            }
        }
        return dp[cur1][0];
    }

    boolean union(int q,int p){
        int proot = find(p);
        int qroot = find(q);
        if(proot==qroot){
            return true;
        }
        id[proot]=qroot;
        return false;
    }

    int find(int q){
        if(q!=id[q]){
            id[q]=find(id[q]);
        }
        return id[q];
    }
    @SuppressWarnings("unused")
    private ArrayList<Integer>[] ng(int n, int m) {
        ArrayList<Integer>[] graph=(ArrayList<Integer>[])new ArrayList[n];
        for(int i=0;i<n;i++) {
            graph[i]=new ArrayList<>();
        }
        for(int i=1;i<=m;i++) {
            int s=in.nextInt()-1,e=in.nextInt()-1;
            graph[s].add(e);
            graph[e].add(s);
        }
        return graph;
    }

    private ArrayList<long[]>[] ngw(int n, int m) {
        ArrayList<long[]>[] graph=(ArrayList<long[]>[])new ArrayList[n];
        for(int i=0;i<n;i++) {
            graph[i]=new ArrayList<>();
        }
        for(int i=1;i<=m;i++) {
            long s=in.nextLong()-1,e=in.nextLong()-1,w=in.nextLong();
            graph[(int)s].add(new long[] {e,w});
            graph[(int)e].add(new long[] {s,w});
        }
        return graph;
    }

    private int ni() {
        return in.nextInt();
    }

    private long nl() {
        return in.nextLong();
    }
    private double nd() {
        return in.nextDouble();
    }
    private String ns() {
        return in.next();
    }

    private long[] na(int n) {
        long[] A=new long[n];
        for(int i=0;i<n;i++) {
            A[i]=in.nextLong();
        }
        return A;
    }

    private int[] nia(int n) {
        int[] A=new int[n];
        for(int i=0;i<n;i++) {
            A[i]=in.nextInt();
        }
        return A;
    }
}

class FastReader
{
    BufferedReader br;
    StringTokenizer st;
    InputStreamReader input;//no buffer
    public FastReader()
    {
        br=new BufferedReader(new InputStreamReader(System.in));
    }

    public FastReader(boolean isBuffer)
    {
        if(!isBuffer){
            input=new InputStreamReader(System.in);
        }else{
            br=new BufferedReader(new InputStreamReader(System.in));
        }
    }

    public boolean hasNext(){
        try{
            String s=br.readLine();
            if(s==null){
                return  false;
            }
            st=new StringTokenizer(s);
        }catch(IOException e){
            e.printStackTrace();
        }
        return true;
    }

    public String next()
    {
        if(input!=null){
            try {
                StringBuilder sb=new StringBuilder();
                int ch=input.read();
                while(ch=='\n'||ch=='\r'||ch==32){
                    ch=input.read();
                }
                while(ch!='\n'&&ch!='\r'&&ch!=32){
                    sb.append((char)ch);
                    ch=input.read();
                }
                return sb.toString();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        while(st==null || !st.hasMoreElements())//回车,空行情况
        {
            try {
                st = new StringTokenizer(br.readLine());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return st.nextToken();
    }

    public int nextInt()
    {
        return (int)nextLong();
    }

    public long nextLong() {
        try {
            if(input!=null){
                long ret=0;
                int b=input.read();
                while(b<'0'||b>'9'){
                    b=input.read();
                }
                while(b>='0'&&b<='9'){
                    ret=ret*10+(b-'0');
                    b=input.read();
                }
                return ret;
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return Long.parseLong(next());
    }

    public double nextDouble()
    {
        return Double.parseDouble(next());
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值