【SPOJ】QTREE6(Link-Cut-Tree)

【SPOJ】QTREE6(Link-Cut-Tree)

题面

Vjudge

题解

很神奇的一道题目
我们发现点有黑白两种,又是动态加边/删边
不难想到\(LCT\)

最爆力的做法,显然是每次修改单点颜色的时候
暴力修改当前点和它的父亲以及儿子之间的连边状态

但是这样显然是假的(菊花树了解一下)

怎么优化呢?
对于每次操作,我们考虑如何只修改一次。
对于树上的一个结点,如果只修改一次,显然是修改和其父亲的状态。
那么,我们在考虑\(LCT\)的连边操作的时候,
如果当前点变色,那么就只修改和它父亲的连边。
这样怎么算答案呢?
如果我们确定树是一棵有根树
那么,我们只需要找到当前点深度最浅的父亲
这个父亲在当前颜色的树上的儿子个数显然就是答案

所以,我们只需要每次只修改当前点和其父亲的关系就行了。
但是要注意一个问题,因为强制是有根树了。
所以打死都不能有\(makeroot\)操作
所以\(link,cut\)之类的都要魔改一发了。。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 111111
#define ls (t[x].ch[0])
#define rs (t[x].ch[1])
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
struct Line{int v,next;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
struct Link_Cut_Tree
{
    struct Node
    {
        int ch[2],ff;
        int size,sum;
        int rev;
    }t[MAX];
    bool isroot(int x){return t[t[x].ff].ch[0]!=x&&t[t[x].ff].ch[1]!=x;}
    void pushup(int x){t[x].sum=t[ls].sum+t[rs].sum+t[x].size+1;}
    void rotate(int x)
    {
        int y=t[x].ff,z=t[y].ff;
        int k=t[y].ch[1]==x;
        if(!isroot(y))t[z].ch[t[z].ch[1]==y]=x;t[x].ff=z;
        t[y].ch[k]=t[x].ch[k^1];t[t[x].ch[k^1]].ff=y;
        t[x].ch[k^1]=y;t[y].ff=x;
        pushup(y);pushup(x);
    }
    void Splay(int x)
    {
        while(!isroot(x))
        {
            int y=t[x].ff,z=t[y].ff;
            if(!isroot(y))
                (t[y].ch[0]==x)^(t[z].ch[0]==y)?rotate(x):rotate(y);
            rotate(x);
        }
        pushup(x);
    }
    void access(int x)
    {
        for(int y=0;x;y=x,x=t[x].ff)
        {
            Splay(x);t[x].size+=t[rs].sum-t[y].sum;
            rs=y;pushup(x);
        }
    }
    void link(int x,int y){if(!y)return;access(y);Splay(x);Splay(y);t[x].ff=y;t[y].size+=t[x].sum;pushup(y);}
    void cut(int x,int y){if(!y)return;access(x);Splay(x);ls=t[ls].ff=0;pushup(x);}
    int findroot(int x){access(x);Splay(x);while(ls)x=ls;Splay(x);return x;}
}LCT[2];
int n,m,fa[MAX],c[MAX];
void dfs(int u,int ff)
{
    for(int i=h[u];i;i=e[i].next)
    {
        int v=e[i].v;if(v==ff)continue;
        LCT[1].link(v,u);fa[v]=u;
        dfs(v,u);
    }
}
int main()
{
    n=read();
    for(int i=1;i<=n;++i)c[i]=1;
    for(int i=1,u,v;i<n;++i)u=read(),v=read(),Add(u,v),Add(v,u);
    dfs(1,0);
    m=read();
    while(m--)
    {
        int opt=read(),x=read();
        if(opt)LCT[c[x]].cut(x,fa[x]),c[x]^=1,LCT[c[x]].link(x,fa[x]);
        else
        {
            LCT[c[x]].access(x);
            int ff=LCT[c[x]].findroot(x);
            if(c[ff]==c[x])printf("%d\n",LCT[c[x]].t[ff].sum);
            else printf("%d\n",LCT[c[x]].t[LCT[c[x]].t[ff].ch[1]].sum);
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/cjyyb/p/8747978.html

时间: 2024-08-28 10:58:26

【SPOJ】QTREE6(Link-Cut-Tree)的相关文章

BZOJ 题目2049: [Sdoi2008]Cave 洞穴勘测(link cut tree)

2049: [Sdoi2008]Cave 洞穴勘测 Time Limit: 10 Sec  Memory Limit: 259 MB Submit: 4698  Solved: 2107 [Submit][Status][Discuss] Description 辉辉热衷于洞穴勘测.某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道连接了恰好两个洞穴.假如两个洞穴可以通过一条或者多条通道按一定顺序连接

BZOJ 题目2002: [Hnoi2010]Bounce 弹飞绵羊(link cut tree)

2002: [Hnoi2010]Bounce 弹飞绵羊 Time Limit: 10 Sec  Memory Limit: 259 MB Submit: 5421  Solved: 2863 [Submit][Status][Discuss] Description 某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏.游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹

【SPOJ】QTREE7(Link-Cut Tree)

[SPOJ]QTREE7(Link-Cut Tree) 题面 洛谷 Vjudge 题解 和QTREE6的本质是一样的:维护同色联通块 那么,QTREE6同理,对于两种颜色分别维护一棵\(LCT\) 每次只修改和它父亲的连边. 考虑如何维护最大值 因为每次\(access\)会删去一个数,所以我们肯定不能够只维护最大值. 因此,对于每一个节点,额外维护一个\(multiset\)(当然,可删堆,\(map\)之类的也行) 每次用\(multiset\)维护虚子树的最值,拿过去更新即可. 最后的答案

HDOJ 题目3966 Aragorn&#39;s Story(Link Cut Tree成段加减点权,查询点权)

Aragorn's Story Time Limit: 10000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 5505    Accepted Submission(s): 1441 Problem Description Our protagonist is the handsome human prince Aragorn comes from The Lor

【BZOJ1095】捉迷藏(动态点分治)

[BZOJ1095]捉迷藏(动态点分治) 题面 BZOJ 题解 动态点分治板子题 假设,不考虑动态点分治 我们来想怎么打暴力: \(O(n)DP\)求树的最长链 一定都会.不想解释了 所以,利用上面的思想 对于每个点,维护子树到他的最长链 以及子树到他的次长链 把这两个玩意拼起来就可能是答案啦 所以,每个点维护两个堆 一个维护子树上的点到他的距离 一个维护所有子树的前面那个堆的最大值 也就是所以子树中,到达当前点的最长链 再在全局维护一个堆 记录每个点最长链和次长链的和 这样子就可以动态的维护了

【BZOJ3730】震波(动态点分治)

[BZOJ3730]震波(动态点分治) 题面 BZOJ 题意 给定一棵树, 每次询问到一个点的距离\(<=K\)的点的权值之和 动态修改权值, 强制在线 题解 正常的\(DP\)??? 很简单呀. 每次暴力往父亲跳,不断的加值, 然后容斥一下就行了 现在要动态维护 就维护一下动态点分治 但是现在记录起来没那么容易了 于是开两棵线段树 每次做一下差 不断暴跳点分树的父亲就行啦 #include<iostream> #include<cstdio> #include<cst

【CF17E】Palisection(回文树)

[CF17E]Palisection(回文树) 题面 洛谷 题解 题意: 求有重叠部分的回文子串对的数量 所谓正难则反 求出所有不重叠的即可 求出以一个位置结束的回文串的数量 和以一个位置为开始的回文串的数量 然后对应的乘一下就行了 求法我用的是回文树 维护每个节点到根节点的距离, 就是回文后缀的数量 CF上的空间是\(128MB\) 卡的很 所以所有的连边考虑用邻接表来代替 #include<iostream> #include<cstdio> #include<cstdl

【LeetCode】BFS(共43题)

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica } [101]Symmetric Tree 判断一棵树是不是对称. 题解:直接递归判断了,感觉和bfs没有什么强联系,当然如果你一定要用queue改写的话,勉强也能算bfs. // 这个题目的重点是 比较对象是 左子树的左儿子和右子树的右儿子, 左子树的右儿子和右子树的左儿子.不要搞错. // 直接中序遍历的话会有错的情况,最蠢的情况是数字标注改一改.. 1 /** 2

【LeetCode】树(共94题)

[94]Binary Tree Inorder Traversal [95]Unique Binary Search Trees II (2018年11月14日,算法群) 给了一个 n,返回结点是 1 - n 的所有形态的BST. 题解:枚举每个根节点 r, 然后递归的生成左右子树的所有集合,然后做笛卡尔积. 1 /** 2 * Definition for a binary tree node. 3 * struct TreeNode { 4 * int val; 5 * TreeNode *