Codeforces 482E ELCA (LCT)

题目链接

http://codeforces.com/contest/482/problem/E

题解

T2智商题T3大LCT题,我一个也不会= =
CF的标算好像是分块?反正现在LCT都普及了就用LCT好了。

首先算期望推个式子,易得答案为\(\sum_u a[u](sz[u]^2-\sum_{v\in son[u]} sz[v]^2)\) (\(sz\)为子树大小),令求和的那个东西等于\(f[u]\)
并且如果往一个\(u\)里新添一个儿子\(v\),增添后的子树大小是\(sz[v]\), 那么新增的答案是\(a[u]sz[v](sz[u]-sz[v])\)
然后我们要支持换父亲,动态维护这个东西

后面的就是莽上一个LCT。
这个只能详见代码,解释一下代码里变量的含义
对于一个Splay结构体\(u\):
\(ans\): 这个点Splay的子树中所有点的\(f\)之和。Splay的根的\(ans\)就是要求的答案。
\(sz01\): 这个点所有虚子树的大小之和加上本身的\(1\)。(\(sz\)是原树中子树大小)
\(sz02\): 这个点本身所有虚子树的大小平方和,不包括本身。
\(ans0\): 这个点本身所有虚儿子的\(ans\)之和。
\(sz11\): 这个点所有实儿子和虚儿子的大小之和。
\(sum\): 这个点所有实儿子\(v\)的\(sz01[v]\times a[v]\)之和。
这些变量看起来有些匪夷所思,那么我们看下怎么维护。
首先,所有对虚儿子求和的变量(也就是名字里带0的三个)都在access时处理,pushup时无需处理。
现在考虑pushup那个函数,前两行处理的是\(sz11\)和\(sum\), 这个根据定义求就行,也没有什么好说的。
后四行是重点——\(ans\)的更新。

我们把\(ans[u]\)分了四部分统计。(选两个点\(x\)和\(y\)求\(a[lca(x,y)]\)之和)
第一部分: 两个点都选在\(u\)的祖先(splay左子树内),或都选在splay右子树内,或都选在\(u\)的同一棵虚子树内。

spl[u].ans = spl[ls].ans+spl[u].ans0+spl[rs].ans;

第二部分: 两个点选在\(u\)的两棵不同虚子树内。

spl[u].ans += (spl[u].sz01*spl[u].sz01-spl[u].sz02)*a[u];

第三部分: 两个点之一选在\(u\)的虚子树内,另一个点选在\(u\)的splay右子树内。这样LCA一定是\(u\).

spl[u].ans += 2ll*spl[u].sz01*spl[rs].sz11*a[u];

第四部分: 两个点之一选在\(u\)的祖先(splay左子树)或其虚子树内,另一个选在\(u\)的整个splay子树及子树内所有点的虚子树去掉\(u\)上方(splay左子树内及其虚子树)的部分,也就是\(u\)及其下方(splay右子树内)所有点及其虚子树内。

spl[u].ans += 2ll*spl[ls].sum*(spl[u].sz11-spl[ls].sz11);

于是就做完了。(第四部分确实有点复杂)

最后膜拜一发考场切此题的新初三巨佬
zjr nb!

代码

#include<cstdio>
#include<cstdlib>
#include<iostream>
#define llong long long
using namespace std;

void read(int &x)
{
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}

const int N = 1e5;
struct SplayNode
{
    int son[2],fa; llong sz01,sz11,sz02,sum,ans0,ans;
} spl[N+3];
int fa[N+3];
llong a[N+3];
int n,q;

bool isroot(int u) {return spl[spl[u].fa].son[0]!=u && spl[spl[u].fa].son[1]!=u;}

void pushup(int u)
{
    int ls = spl[u].son[0],rs = spl[u].son[1];
    spl[u].sz11 = spl[ls].sz11+spl[u].sz01+spl[rs].sz11;
    spl[u].sum = spl[ls].sum+spl[rs].sum+spl[u].sz01*a[u];
    spl[u].ans = spl[ls].ans+spl[u].ans0+spl[rs].ans;
    spl[u].ans += (spl[u].sz01*spl[u].sz01-spl[u].sz02)*a[u];
    spl[u].ans += 2ll*spl[u].sz01*spl[rs].sz11*a[u];
    spl[u].ans += 2ll*spl[ls].sum*(spl[u].sz11-spl[ls].sz11);
}

void rotate(int u)
{
    int x = spl[u].fa,y = spl[x].fa;
    spl[u].fa = y;
    if(!isroot(x)) {spl[y].son[x==spl[y].son[1]] = u;}
    int dir = u==spl[x].son[0];
    spl[x].son[dir^1] = spl[u].son[dir];
    if(spl[u].son[dir]) {spl[spl[u].son[dir]].fa = x;}
    spl[u].son[dir] = x; spl[x].fa = u;
    pushup(x); pushup(u);
}

void splaynode(int u)
{
    while(!isroot(u))
    {
        int x = spl[u].fa,y = spl[x].fa;
        if(!isroot(x)) {x==spl[y].son[1] ^ u==spl[x].son[1] ? rotate(u) : rotate(x);}
        rotate(u);
    }
    pushup(u);
}

void access(int u)
{
    for(int i=0; u; i=u,u=spl[u].fa)
    {
        splaynode(u);
        int ls = spl[u].son[0],rs = spl[u].son[1];
        spl[u].sz01 += spl[rs].sz11;
        spl[u].sz02 += spl[rs].sz11*spl[rs].sz11;
        spl[u].ans0 += spl[rs].ans; //not ans0
        rs = spl[u].son[1] = i;
        spl[u].sz01 -= spl[rs].sz11;
        spl[u].sz02 -= spl[rs].sz11*spl[rs].sz11;
        spl[u].ans0 -= spl[rs].ans;
        pushup(u);
    }
}

void link(int u,int v)
{
    access(v); splaynode(v);
    access(u); splaynode(u); //in order to pushup
    spl[u].sz01 += spl[v].sz11;
    spl[u].sz02 += spl[v].sz11*spl[v].sz11;
    spl[u].ans0 += spl[v].ans;
    spl[v].fa = u;
    pushup(u);
}

void cut(int u,int v)
{
    access(u); splaynode(u);
    splaynode(v); //v is in the virtual subtree of u
    spl[u].sz01 -= spl[v].sz11;
    spl[u].sz02 -= spl[v].sz11*spl[v].sz11;
    spl[u].ans0 -= spl[v].ans;
    spl[v].fa = 0;
    pushup(u);
}

bool isanc(int u,int v) //if u is ancestor of v
{
    access(v); splaynode(v);
    splaynode(u);
    if(!isroot(v)) return true;
    return false;
}

void printans(llong x)
{
    double ans = (double)x/(double)n/(double)n;
    printf("%.12lf\n",ans);
}

int main()
{
    scanf("%d",&n);
    for(int i=2; i<=n; i++) scanf("%d",&fa[i]);
    for(int i=1; i<=n; i++) scanf("%I64d",&a[i]);
    for(int i=1; i<=n; i++) spl[i].sz11 = spl[i].sz01 = 1ll,spl[i].ans = spl[i].sum = a[i];
    for(int i=2; i<=n; i++)
    {
        link(fa[i],i);
    }
    access(1); splaynode(1);
    printans(spl[1].ans);
    scanf("%d",&q);
    for(int i=1; i<=q; i++)
    {
        char opt[5]; scanf("%s",opt+1);
        if(opt[1]=='P')
        {
            int x,y; scanf("%d%d",&x,&y);
            if(isanc(x,y)) {swap(x,y);}
            cut(fa[x],x);
            fa[x] = y;
            link(fa[x],x);
            access(1); splaynode(1);
            printans(spl[1].ans);
        }
        else if(opt[1]=='V')
        {
            int x; llong y; scanf("%d%I64d",&x,&y);
            access(x); splaynode(x);
            a[x] = y;
            pushup(x);
            printans(spl[x].ans);
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/suncongbo/p/11330695.html

时间: 2024-10-25 17:40:51

Codeforces 482E ELCA (LCT)的相关文章

[Codeforces 603E]Pastoral Oddities(LCT)

[Codeforces 603E]Pastoral Oddities(LCT) 题面 图中有n个孤立点,依次向图中加入m条带权无向边.使得图中每个点的度数均为奇数的边集是合法的,其权值定义为集合中的最大边权.每次加入边后,询问权值最小的合法边集的权值,不存在合法边集时输出?1. \(n \leq 10^5,m \leq 3\times 10^5\) 分析 手玩样例可得:图存在合法边集,当且仅当每个连通块的大小为偶数 证明: 先证明充分性:假设某合法连通块大小为奇数,那么该块的总度数是奇数.但所有

CodeForces gym Nasta Rabbara lct

Nasta Rabbara 题意:简单来说就是, 现在有 n个点, m条边, 每次询问一个区间[ l ,  r ], 将这个区间的所有边都连上, 如果现在的图中有奇数环, 就输出 "Impossible", 否者就输出 "possible". 题解: 步骤1:我们先找出每个最小的 [ l,  r]  当这个区间的边都出现后, 就会出现一个奇数环. 步骤2:问题就变成了对于一次询问 [ L, R ]  是否存在上面的一个区间 被完全覆盖. 对于步骤1来说:需要加边和删

Codeforces 603E Pastoral Oddities

传送门:http://codeforces.com/problemset/problem/603/E [题目大意] 给出$n$个点,$m$个操作,每个操作加入一条$(u, v)$长度为$l$的边. 对于每次操作后,求出一个边集,使得每个点度数均为奇数,且边集的最大边最小. $n \leq 10^5, m \leq 3 * 10^5$ [题解] 有结论:满足条件(每个点度数均为奇数),当且仅当每个连通块大小都是偶数(容易证明,从下往上,调整法). 那么显然可以LCT维护连通性,连通块大小以及最大边

codeforces round #321 (div2)

codeforces#321(div2) A题:水题. #include<bits/stdc++.h> #define REP(i,a,b) for(int i=a;i<=b;i++) using namespace std; typedef long long ll; const int maxn=1000100; ll n,a[maxn]; int main() { //freopen("in.txt","r",stdin); while(ci

@codeforces - [email&#160;protected] Mashmokh&#39;s Designed Problem

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一棵 n 个点的树,每个点的儿子是有序的. 现给定 m 次操作,每次操作是下列三种中的一种: (1)给定 u, v,询问 u, v 之间的距离. (2)给定 v, h,断开 v 到父亲的边,将 v 这棵子树加入到它的第 h 个祖先的最后一个儿子. (3)给定 k,询问在当前这棵树上

【codeforces 718E】E. Matvey&#39;s Birthday

题目大意&链接: http://codeforces.com/problemset/problem/718/E 给一个长为n(n<=100 000)的只包含‘a’~‘h’8个字符的字符串s.两个位置i,j(i!=j)存在一条边,当且仅当|i-j|==1或s[i]==s[j].求这个无向图的直径,以及直径数量. 题解:  命题1:任意位置之间距离不会大于15. 证明:对于任意两个位置i,j之间,其所经过每种字符不会超过2个(因为相同字符会连边),所以i,j经过节点至多为16,也就意味着边数至多

Codeforces 124A - The number of positions

题目链接:http://codeforces.com/problemset/problem/124/A Petr stands in line of n people, but he doesn't know exactly which position he occupies. He can say that there are no less than a people standing in front of him and no more than b people standing b

Codeforces 841D Leha and another game about graph - 差分

Leha plays a computer game, where is on each level is given a connected graph with n vertices and m edges. Graph can contain multiple edges, but can not contain self loops. Each vertex has an integer di, which can be equal to 0, 1 or  - 1. To pass th

luoguP2590 [ZJOI2008]树的统计 [树链剖分] [TLE的LCT]

题目描述 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w. 我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身 输入输出格式 输入格式: 输入文件的第一行为一个整数n,表示节点的个数. 接下来n – 1行,每行2个整数a和b,表示节点a和节点b之