zoukankan      html  css  js  c++  java
  • noip模拟赛 洗衣

    分析:好神的一道题啊.对每棵树建个图跑一下floyd可以有40分,想要打出正解就得对树有比较深的认识了.

          每次新生成一棵树都是由两棵树i,j拼成的,答案为原来两棵树的答案和+i中每个点到j中每个点的距离和.显然这个距离和不能直接算,涉及到求整体的值,通常考虑每条边的贡献.设i,j两棵树的连接点为p,q,边长为l.l对答案的贡献是i的点数*j的点数*l.i中每个点要走到j里面,必须要先走到p上,i中的每个点对它走过的路径的贡献是j的点数*路径的边权,因为每条路径都被若干个点经过,所以算出i中所有点到p的距离和乘上j的点数就是i中的边对答案的贡献.j中所有边对答案的贡献也是这样算的.

          那么怎么求树i中所有点到t的距离和呢?还是可以把树i拆分成两棵子树x,y.如果p在i的左子树中,那么就是连接点p到t的距离加上连接x,y的边的长度l的和乘上y中的点数加上y中所有点到q的距离和加上x中所有点到t的距离和.这实际上就相当于拆成了3部分:1是x里面的所有点到t去,2是y中的所有点到q去,3是y中所有点聚集到q后一起到t.如果p在i的右子树中,做法类似,递归地算下去.

          那么怎么求p到t的距离呢?还是分两个点所在的树来讨论.如果p,t在同一子树,就在同一子树内递归算,如果在不同子树就先到连接点,然后再走过去,和之前的做法差不多.

          因为求得的东西要经常被用到,所以需要记录状态的,可是m≤60,点的个数可能多达2^60个,这该怎么记录......其实需要记录的只有连接点的状态,但是连接点的编号可能很大,也会爆空间,一个很神的技巧就是在map中保存状态orz.开long long就能存下了.

          丧心病狂的一道题,求两棵树任意两点之间的距离和的做法在树形dp中比较常见,需要记一下.黑科技map可以记录不多,但是表示起来很大的状态.

    #include <map>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long ll;
    
    const int mod = 1e9 + 7;
    
    struct node
    {
        ll p, p1, p2;
        node(){}
        node(ll a, ll b, ll c)
        {
            p = a;
            if (b < c)
            {
                p1 = b;
                p2 = c;
            }
            else
            {
                p1 = c;
                p2 = b;
            }
        }
        bool operator < (const node & a) const {
            if (p != a.p)
                return p < a.p;
            if (p1 != a.p1)
                return p1 < a.p1;
            return p2 < a.p2;
        }
    };
    
    int n;
    ll id1[170], id2[170], num1[170], num2[170], l[170], sizee[170];
    ll ans[170];
    
    map <pair< ll, ll > , ll > m;
    map <node, ll> m2;
    
    ll solve2(ll p, ll p1, ll p2)
    {
        if (!p)
            return 0;
        if (p1 == p2)
            return 0;
        node temp = node(p, p1, p2);
        if (m2.count(temp))
            return m2[temp];
        if (p1 < sizee[id1[p]])
        {
            if (p2 < sizee[id1[p]])
                m2[temp] = solve2(id1[p], p1, p2);
            else
                m2[temp] = (solve2(id1[p], num1[p], p1) + solve2(id2[p], num2[p], p2 - sizee[id1[p]]) + l[p]) % mod;
        }
        else
        {
            if (p2 < sizee[id1[p]])
                m2[temp] = (solve2(id1[p], num1[p], p2) + solve2(id2[p], num2[p], p1 - sizee[id1[p]]) + l[p]) % mod;
            else
                m2[temp] = solve2(id2[p], p1 - sizee[id1[p]], p2 - sizee[id1[p]]);
        }
        return m2[temp];
    }
    
    ll solve(ll p, ll n)
    {
        if (p == 0)
            return 0;
        pair <ll, ll> t = make_pair(p, n);
        if (m.count(t))
            return m[t];
        if (n < sizee[id1[p]])
        {
            ll temp1 = (solve2(id1[p], num1[p], n) + l[p]) % mod;
            ll temp2 = temp1 * (sizee[id2[p]] % mod) % mod;
            ll temp3 = (solve(id2[p], num2[p]) + solve(id1[p], n)) % mod;
            m[t] = (temp2 + temp3) % mod;
        }
        else
        {
            ll temp1 = (solve2(id2[p], num2[p], n - sizee[id1[p]]) + l[p]) % mod;
            ll temp2 = temp1 * (sizee[id1[p]] % mod) % mod;
            ll temp3 = (solve(id1[p], num1[p]) + solve(id2[p], n - sizee[id1[p]])) % mod;
            m[t] = (temp2 + temp3) % mod;
        }
        return m[t];
    }
    
    int main()
    {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
            scanf("%lld%lld%lld%lld%lld", &id1[i], &id2[i], &num1[i], &num2[i], &l[i]);
        sizee[0] = 1;
        for (int i = 1; i <= n; i++)
            sizee[i] = sizee[id1[i]] + sizee[id2[i]];
        for (int i = 1; i <= n; i++)
        {
            ll temp1 = solve(id1[i], num1[i]) * (sizee[id2[i]] % mod) % mod;
            ll temp2 = (sizee[id1[i]] % mod )* (sizee[id2[i]] % mod) % mod * l[i] % mod;
            ll temp3 = solve(id2[i], num2[i]) * (sizee[id1[i]] % mod) % mod;
            ans[i] = (((((((temp1 + temp2) % mod) + temp3) % mod) + ans[id1[i]]) % mod) + ans[id2[i]]) % mod;
        }
        for (int i = 1; i <= n; i++)
            printf("%lld
    ", ans[i]);
    return 0;
    }
  • 相关阅读:
    递归获取指定盘符下的所有文件及文件夹
    单例模式和多线程有没有关系?
    eclipse启动tomcat时设置端口
    dozer转化对象
    枚举
    dubbo
    json
    配网失败问题
    esp_err_t esp_event_loop_init(system_event_cb_t cb, void *ctx);
    base64编码
  • 原文地址:https://www.cnblogs.com/zbtrs/p/7765047.html
Copyright © 2011-2022 走看看