• bzoj3091 城市旅行


    3091: 城市旅行

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 2006  Solved: 655
    [Submit][Status][Discuss]

    Description

    Input

    Output

    Sample Input

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

    Sample Output

    16/3
    6/1

    HINT

    对于所有数据满足 1<=N<=50,000 1<=M<=50,000 1<=Ai<=10^6 1<=D<=100 1<=U,V<=N

    Source

    wyx528命题

    分析:好题!

       前两个操作说明这道题要用到LCT. 最大的问题就是如何实现操作四? 需要在splay上维护一些信息,使得能够快速统计出期望,并且支持合并和操作三的修改.

       维护什么信息呢? 对于区间[x,y](路径上的,不是序号上的),其中任意两个点之间路径的点权和加起来/这个区间的点对数就是答案了. 分母很好计算:令len = (y - x + 1). 那么分母就是len * (len + 1) / 2. 关键在于如何计算分子.

       对于总体求和,不能单独去考虑路径,而要考虑每一个点对答案的贡献,加起来就是答案了.  具体来说,假设区间是[1,n],那么答案就是a1 * 1 * n + a2 * 2 * (n - 1) + a3 * 3 * (n - 2) + ...... + an * n * 1.能维护出每个区间的答案就好了.

       这显然是不能直接用一个数组来维护的.  令ans表示上述式子的结果.  当前区间的ans可以通过左右子区间合并再加上一些东西得到. 如果当前区间有7个数(包括根),左区间有4个数,右区间有2个数.

       考虑左区间: ans[lson] = a1 * 1 * 4 + a2 * 2 * 3 + a3 * 3 * 2 + a4 * 4 * 1.  加上右区间和根后,就变成了a1 * 1 * 7 + a2 * 2 * 6 + a3 * 3 * 5 + a4 * 4 * 4. 如果令lsum = a1 * 1 + a2 * 2 + a3 * 3 + a4 * 4 = Σai * i,那么ans[lson]就是加上了lsum[lson] * (size[rson] + 1). size[rson]是右子树的大小. 这样ans就能被维护出来了.

       lsum,rsum都能够用类似的方法分析得到对应的式子,并且维护.

       对于操作三要怎么做呢? lsum和rsum相当于加上了d * (1 + 2 + 3 + ...... + n),可以用等差数列求和公式搞一搞. ans加上了d * (1 * n + 2 * (n - 1) + 3 * (n - 2) + ...... + n * 1). 括号里式子的结果等于n * (n + 1) * (n + 2) / 6.

       细节:这道题翻转操作是要交换lsum和rsum的. 不能只打一个标记就完了. 而要即时翻转.  可以对比两份代码的不同:传送门1,传送门2.它们的区别就是一个在pushdown的时候修改当前节点,一个直接修改,并修改标记,在pushdown的时候修改子树.

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long ll;
    const ll maxn = 100010;
    ll n,m,head[maxn],to[maxn],nextt[maxn],tot = 1,a[maxn],fa[maxn];
    ll sizee[maxn],sum[maxn],lsum[maxn],rsum[maxn],ans[maxn],w[maxn];
    ll rev[maxn],son[maxn][2],sta[maxn],add[maxn];
    
    void pushup(int x)
    {
        int lc = son[x][0],rc = son[x][1];
        sizee[x] = sizee[lc] + 1 + sizee[rc];
        sum[x] = sum[lc] + sum[rc] + w[x];
        lsum[x] = lsum[lc] + w[x] * (sizee[lc] + 1) + lsum[rc] + sum[rc] * (sizee[lc] + 1);
        rsum[x] = rsum[rc] + w[x] * (sizee[rc] + 1) + rsum[lc] + sum[lc] * (sizee[rc] + 1);
        ans[x] = ans[lc] + ans[rc] + lsum[lc] * (sizee[rc] + 1) + rsum[rc] * (sizee[lc] + 1) + w[x] * (sizee[lc] + 1) * (sizee[rc] + 1);
    }
    
    void dfs(ll u,ll faa)
    {
        fa[u] = faa;
        for (ll i = head[u];i; i = nextt[i])
        {
            ll v = to[i];
            if (v == faa)
                continue;
            dfs(v,u);
        }
    }
    
    void fan(ll x)
    {
        swap(son[x][0],son[x][1]);
        swap(lsum[x],rsum[x]);
        rev[x] ^= 1;
    }
    
    ll cal1(ll x)
    {
        return x * (x + 1) / 2;
    }
    
    ll cal2(ll x)
    {
        return x * (x + 1) * (x + 2) / 6;
    }
    
    void Add(ll x,ll v)
    {
        w[x] += v;
        add[x] += v;
        sum[x] += v * sizee[x];
        lsum[x] += v * cal1(sizee[x]);
        rsum[x] += v * cal1(sizee[x]);
        ans[x] += v * cal2(sizee[x]);
    }
    
    void pushdown(ll x)
    {
        if (rev[x])
        {
            fan(son[x][0]);
            fan(son[x][1]);
            rev[x] = 0;
        }
        if (add[x])
        {
            Add(son[x][0],add[x]);
            Add(son[x][1],add[x]);
            add[x] = 0;
        }
    }
    
    bool is_root(ll x)
    {
        return son[fa[x]][0] != x && son[fa[x]][1] != x;
    }
    
    bool get(ll x)
    {
        return son[fa[x]][1] == x;
    }
    
    void turn(ll x)
    {
        ll y = fa[x];
        ll z = fa[y];
        ll temp = get(x);
        if (!is_root(y))
            son[z][son[z][1] == y] = x;
        fa[x] = z;
        son[y][temp] = son[x][temp ^ 1];
        fa[son[y][temp]] = y;
        son[x][temp ^ 1] = y;
        fa[y] = x;
        pushup(y);
        pushup(x);
    }
    
    void splay(ll x)
    {
        ll top = 0;
        sta[++top] = x;
        for (ll y = x; !is_root(y); y = fa[y])
            sta[++top] = fa[y];
        for (ll i = top; i >= 1; i--)
            pushdown(sta[i]);
        ll temp;
        for (; !is_root(x); turn(x))
        {
            if (!is_root(temp = fa[x]))
            {
                if (get(x) == get(temp))
                    turn(temp);
                else
                    turn(x);
            }
        }
    }
    
    void Access(ll x)
    {
        ll t = 0;
        for (; x;t = x,x = fa[x])
        {
            splay(x);
            son[x][1] = t;
            pushup(x);
        }
    }
    
    void Reverse(ll x)
    {
        Access(x);
        splay(x);
        fan(x);
    }
    
    void Link(ll x,ll y)
    {
        Reverse(x);
        fa[x] = y;
        splay(x);
    }
    
    void Cut(ll x,ll y)
    {
        Reverse(x);
        Access(y);
        splay(y);
        fa[x] = son[y][0] = 0;
    }
    
    ll find(ll x)
    {
        Access(x);
        splay(x);
        while (son[x][0])
            x = son[x][0];
        return x;
    }
    
    void update(ll x,ll y,ll d)
    {
        if (find(x) != find(y))
            return;
        Reverse(x);
        Access(y);
        splay(y);
        Add(y,d);
    }
    
    ll gcd(ll a,ll b)
    {
        if (!b)
            return a;
        return gcd(b,a % b);
    }
    
    void query(ll x,ll y)
    {
        if (find(x) != find(y))
        {
            printf("%d
    ",-1);
            return;
        }
        Reverse(x);
        Access(y);
        splay(y);
        ll a = ans[y],b = sizee[y] * (sizee[y] + 1) / 2;
        ll g = gcd(a,b);
        printf("%lld/%lld
    ",a / g,b / g);
    }
    
    int main()
    {
        scanf("%lld%lld",&n,&m);
        for (ll i = 1; i <= n; i++)
        {
            scanf("%lld",&a[i]);
            sizee[i] = 1;
            sum[i] = lsum[i] = rsum[i] = ans[i] = w[i] = a[i];
        }
        for (int i = 1; i < n; i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            Link(x,y);
        }
        while (m--)
        {
            ll id,u,v,d;
            scanf("%lld",&id);
            if (id == 1)
            {
                scanf("%lld%lld",&u,&v);
                if (find(u) == find(v))
                    Cut(u,v);
            }
            if (id == 2)
            {
                scanf("%lld%lld",&u,&v);
                if (find(u) != find(v))
                    Link(u,v);
            }
            if (id == 3)
            {
                scanf("%lld%lld%lld",&u,&v,&d);
                update(u,v,d);
            }
            if (id == 4)
            {
                scanf("%lld%lld",&u,&v);
                query(u,v);
            }
        }
    
        return 0;
    }

       

  • 相关阅读:
    通过char与varchar的区别,学习可变长的字符类型
    laravel向视图传递变量
    MySQL数据库几种常用的索引类型使用介绍
    Java小知识点总结01
    好的代码习惯
    刻意练习
    算法
    经常复习
    kibana查询语法 使用教程
    工作思考
  • 原文地址:https://www.cnblogs.com/zbtrs/p/8533576.html
Copyright © 2020-2023  润新知