牛客编程巅峰赛S2第10场 - 钻石&王者 做题记录

时隔半个月。。。终于搞懂C题了。。

先上原来写的超时代码 ,卡在90%。

写了一个暴力dfs , 应该是O(N^2)的
主要是没有用到与运算的性质,没有按位与。 简单粗暴的与运算导致重复走了很多个点。

    long res =0;
    public long solve (int n, int[] u, int[] v, int[] p) {
        Map> book = new HashMap();
        for(int i=0;i<n;++i){
            book.put(i,new ArrayList());
        }
        for(int i=0;i<u.length;++i){
            int start = u[i];
            int end =v[i];
            book.get(start).add(end);
            book.get(end).add(start);
        }
        for(int i=0;i<n;++i){
            //以点i为起点
            long cur =-1;
            boolean[] flag = new boolean[n];
            fun(book,p,cur,flag,i);
        }
        long r=  0;
        for(int pp:p){
            r+= pp;
        }

        return (res -r)/2+r;
    }

    public void fun(Map> book,int[] p,long cur,boolean[] flag,int i){
        if(flag[i] || cur ==0){
            return;
        }
        if(cur ==-1){
            //起点
            cur = p[i];
        }else{
            cur = cur & p[i];
        }
        res += cur;
        flag[i]=true;
        List next = book.get(i);
        for(int n : next){
            fun(book,p,cur,flag,n);
        }
    }

按照与运算+连通块的性质, 写bfs
(按位与 : 例如 15 = 8+4+2+1 , 即 fun(15) = fun(1<<3) + fun(1<<2) + fun(1<<1) + fun(1<<0) )

    public long solve (int n, int[] u, int[] v, int[] p) {
        long res =0;
        List<Integer>[] edges = new ArrayList[n];
        for(int i=0;i<edges.length;++i){
            edges[i] = new ArrayList<>();
        }
        for(int i=0;i<u.length;++i){
            edges[u[i]].add(v[i]);
            edges[v[i]].add(u[i]);
        }

        int base =1;
        for(int i=0;i<20;++i){
            //求当前base的连通块数量
            boolean[] vis = new boolean[n];
            for(int j=0;j<n;++j){
                int cnt = bfs(edges,p,j,base,vis);
                res += ((long)cnt*(cnt+1))/2*base;
            }
            base = base <<1;
        }
        return res;
    }

    public int bfs(List<Integer>[] edges,int[] p ,int cur,int base,boolean[] vis){
        if(vis[cur] || (p[cur]&base) ==0){
            return 0;
        }
        int res =0;
        Deque<Integer> queue = new LinkedList<>();
        queue.offerLast(cur);
        while(!queue.isEmpty()){
            cur = queue.pollFirst();
            res ++;
            vis[cur] = true;

            for(int edge : edges[cur]){
                if((p[edge]&base)!=0 && !vis[edge]){
                    queue.offerLast(edge);
                }
            }
        }
        return res;
    }

连通块问题也可以使用并查集:

    public long solve (int n, int[] u, int[] v, int[] p) {
        long res =0;
        //按位与,20位
        int base =1;
        for(int cnt =0;cnt<20;++cnt){
            //初始化并查集
            int[] size = new int[n];
            Arrays.fill(size,0);
            int[] f=new int[n];
            for(int i=0;i<n;++i){
                f[i] = i;
            }

            for(int i=0;i<u.length;++i){
                if( (p[u[i]]&base)!=0 && (p[v[i]]&base)!=0 ){
                    //并u[i] v[i]
                    f[find(f,u[i])] = find(f,v[i]);
                }
            }
            for(int i=0;i<n;++i){
                size[find(f,i)]++;
            }
            ArrayTools.printArray(size);
            for(int i=0;i<n;++i){
                if((p[i]&base) !=0){
                    res += (long)size[i]*(size[i]+1)/2*base;
                }
            }
            base =base <<1;
        }
        return res;
    }

    public int find(int[] f,int x){
        if(f[x] ==x){
            return x;
        }
        f[x]=find(f,f[x]);
        return f[x];
    }
全部评论
点赞 回复 分享
发布于 2020-12-31 12:05

相关推荐

CrazyBucket:我今天下午也做梦在招聘会上面试一家小厂,给自己气笑了
点赞 评论 收藏
分享
如题,字节跳动怎么才能看到自己的面评,找hr说看不到
SoulStar:自己应该看不到,这个是字节比较保密的信息,之前有mentor加我,说他能看到,但是不能给我说,给我说了他可能就要被辞退了
点赞 评论 收藏
分享
2 收藏 评论
分享
牛客网
牛客企业服务