zoukankan      html  css  js  c++  java
  • 【点分治】【路径小于等于k的条数】【路径恰好等于k是否存在】

    POJ1741:Tree
    Time Limit: 1000MS   Memory Limit: 30000K
    Total Submissions: 29574   Accepted: 9915

    Description

    Give a tree with n vertices,each edge has a length(positive integer less than 1001). 
    Define dist(u,v)=The min distance between node u and v. 
    Give an integer k,for every pair (u,v) of vertices is called valid if and only if dist(u,v) not exceed k. 
    Write a program that will count how many pairs which are valid for a given tree. 

    Input

    The input contains several test cases. The first line of each test case contains two integers n, k. (n<=10000) The following n-1 lines each contains three integers u,v,l, which means there is an edge between node u and v of length l. 
    The last test case is followed by two zeros. 

    Output

    For each test case output the answer on a single line.

    Sample Input

    5 4
    1 2 3
    1 3 1
    1 4 2
    3 5 1
    0 0
    

    Sample Output

    8

    Source

    求树上路径距离小于等于k的条数。

    点分治即是将树拆开,dfs处理每棵子树的过程。每次找到当前子树的重心,从重心开始分治(即是放弃父亲,不再管之前的祖先。

    这道题可以用容斥计算贡献。先统计出当前整棵树的答案,减去每棵子树重复计算的不成立的答案。【注意】这里的答案都是指经过当前树的根节点的路径。

    如图,如果k是4,,直接统计子树1的答案,会把1到3和1到4这条路径统计进去,而这是不成立的,所以在统计子树2多余答案时,先把1到2这条边权加进2的dis,再统计子树2中满足条件的点对,减去即可。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    
    int bal, asize, sum, n, k, ans;
    
    int tov[20004], nex[20004], h[10005], stot, w[20005];
    
    void add ( int u, int v, int s ) {
        tov[++stot] = v;
        w[stot] = s;
        nex[stot] = h[u];
        h[u] = stot;
    }
    
    int siz[10005], vis[10005];
    
    void find_root ( int u, int f ) {
        siz[u] = 1;
        int res = 0;
        for ( int i = h[u]; i; i = nex[i] ) {
            int v = tov[i];
            if ( v == f || vis[v] ) continue;
            find_root ( v, u );
            siz[u] += siz[v];
            res = max ( res, siz[v] );
        }
        res = max ( res, sum - siz[u] );
        if ( res < asize ) {
            asize = res, bal = u;
        }
    }
    
    int dep[10004], dis[10005];
    
    void get_dep ( int u, int f ) {
        dep[++dep[0]] = dis[u];
        siz[u] = 1;
        for ( int i = h[u]; i; i = nex[i] ) {
            int v = tov[i];
            if ( v == f || vis[v] ) continue;
            dis[v] = dis[u] + w[i];
            get_dep ( v, u );
            siz[u] += siz[v]; 
        }
    }
    
    int cal ( int u, int now ) {
        dis[u] = now; dep[0] = 0;
        get_dep ( u, 0 );
        sort ( dep + 1, dep + dep[0] + 1 );
        int tmp = 0, l = 1, r = dep[0];
        while ( l < r ) {
            if ( dep[l] + dep[r] <= k ) {
                tmp += r - l; l ++;
            } else r --;
        }
        return tmp;
    }
    
    void work ( int u ) {
        ans += cal ( u, 0 );
        vis[u] = 1;
        for ( int i = h[u]; i; i = nex[i] ) {
            int v = tov[i];
            if ( vis[v] ) continue;
            ans -= cal ( v, w[i] );
            sum = siz[v];
            asize = 0x3f3f3f3f;
            find_root ( v, u );
            work ( bal );
        }
    }
    
    int main ( ) {
        while ( scanf ( "%d%d", &n, &k ) == 2 ) {
            if ( n == 0 && k == 0 ) break;
            asize = 0x3f3f3f3f;
            stot = 0; ans = 0;
            memset ( h, 0, sizeof ( h ) );
            memset ( dis, 0, sizeof ( dis ) );
            memset ( vis, 0, sizeof ( vis ) );
            for ( int i = 1; i < n; i ++ ) {
                int a, b, c;
                scanf ( "%d%d%d", &a, &b, &c );
                add ( a, b, c );
                add ( b, a, c ); 
            }
            sum = n;
            find_root ( 1, 1 );
            work ( bal );
            printf ( "%d
    ", ans );
        }
        return 0;
    }

    洛谷P3806: 【模板】点分治1

    题目背景

    感谢hzwer的点分治互测。

    题目描述

    给定一棵有n个点的树

    询问树上距离为k的点对是否存在。

    输入输出格式

    输入格式:

    n,m 接下来n-1条边a,b,c描述a到b有一条长度为c的路径

    接下来m行每行询问一个K

    输出格式:

    对于每个K每行输出一个答案,存在输出“AYE”,否则输出”NAY”(不包含引号)

    输入输出样例

    输入样例#1: 复制
    2 1
    1 2 2
    2
    输出样例#1: 复制
    AYE

    说明

    对于30%的数据n<=100

    对于60%的数据n<=1000,m<=50

    对于100%的数据n<=10000,m<=100,c<=1000,K<=10000000

    这道题和上一道实质一样,不过我换了种写法。在统计当前树答案时,进入每棵子树,先把这棵子树的答案与之前计算过的子树答案(exist)进行比对,如果可以就更新答案,再更新exist数组,这样可以保证不会出现上面图示情况,因为计算当前子树时,不会出现子树内部互相更新的情况。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    
    int n, m, qus[10005];
    
    int stot, tov[200005], nex[200005], h[100005], w[100005];
    void add ( int u, int v, int s ) {
        tov[++stot] = v;
        w[stot] = s;
        nex[stot] = h[u];
        h[u] = stot;
    }
    
    int siz[100005], asize, size, root, vis[100005], maxp[100005];
    void findroot ( int u, int f ) {
        siz[u] = 1;
        for ( int i = h[u]; i; i = nex[i] ) {
            int v = tov[i];
            if ( v == f || vis[v] ) continue;
            findroot ( v, u );
            siz[u] += siz[v];
            maxp[u] = max ( maxp[u], siz[v] );
        }
        maxp[u] = max ( maxp[u], size - siz[u] );
        if ( maxp[u] < maxp[root] ) root = u;
    }
    
    int dep[100005], dis[100005];
    void getdis ( int u, int f ) {
        dep[++dep[0]] = dis[u];
        for ( int i = h[u]; i; i = nex[i] ) {
            int v = tov[i];
            if ( v == f || vis[v] ) continue;
            dis[v] = dis[u] + w[i];
            getdis ( v, u );
        }
    }
    
    bool judge[1005], exist[10000005];
    int q[100005], p;
    void count ( int u ) {
        int p = 0;
        for ( int i = h[u]; i; i = nex[i] ) {
            int v = tov[i];
            if ( vis[v] ) continue;
            
            dep[0] = 0; dis[v] = w[i];
            getdis ( v, u );
            
            for ( int j = dep[0]; j; j -- )
                for ( int k = 1; k <= m; k ++ )
                    if ( qus[k] >= dep[j] )
                    judge[k] |= exist[qus[k]-dep[j]];
        
            for ( int j = dep[0]; j; j -- )
                q[++p] = dep[j], exist[dep[j]] = 1;
        }
        for ( int i = 1; i <= p; i ++ )
            exist[q[i]] = 0;
    }
    
    void work ( int u ) {
        vis[u] = 1; exist[0] = 1;
        count ( u );
        for ( int i = h[u]; i; i = nex[i] ) {
            int v = tov[i];
            if ( vis[v] ) continue;
            size = siz[v]; root = 0;
            findroot ( v, 0 );
            work ( root );
        }
    }
    
    int main ( ) {
        freopen ( "a.in", "r", stdin );
        freopen ( "a.out", "w", stdout );
        scanf ( "%d%d", &n, &m );
        for ( int i = 1; i < n; i ++ ) {
            int a, b, c;
            scanf ( "%d%d%d", &a, &b, &c );
            add ( a, b, c );
            add ( b, a, c );
        }
        
        for ( int i = 1; i <= m; i ++ )
            scanf ( "%d", &qus[i] );
        
        size = n; maxp[root] = n;
        findroot ( 1, 0 );
        work ( root );
        
        for ( int i = 1; i <= m; i ++ )
            if ( judge[i] )
                printf ( "AYE
    " );
            else    printf ( "NAY
    " );
        return 0;
    }
  • 相关阅读:
    进程与线程
    二维数组和指向指针的指针
    _variant_t 到 CString 转换
    1.15 构造数独
    单链表的一些操作
    C++关键字(2)——extern
    the seventh chapter
    Visual C++ 数据库开发的特点
    CString 和 LPCTSTR区别【转】
    2.5 寻找最大的K个数
  • 原文地址:https://www.cnblogs.com/wans-caesar-02111007/p/9501696.html
Copyright © 2011-2022 走看看