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\n",-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\n",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;
}

 

   

posted @ 2018-03-09 14:06  zbtrs  阅读(193)  评论(0编辑  收藏  举报