• [bzoj4530][Bjoi2014]大融合_LCT


    大融合 bzoj-4530 Bjoi-2014

    题目大意:n个点,m个操作,支持:两点连边;查询两点负载:负载。边(x,y)的负载就是将(x,y)这条边断掉后能和x联通的点的数量乘以能和y联通的点的数量。数据保证任意时刻,点和边构成的都是森林或者树。

    注释:$1le n,mle 10^5$。


    想法:新学了一发LCT维护子树信息,更一道例题。

    话说LCT维护子树信息应该怎么做?其实也非常简单。我们只需要将所有的信息都加到父节点上即可。

    具体的,我们除了维护子树和sum之外另维护一个值other,表示这个节点的所有虚儿子的子树和。

    然后这东西在access中将所有儿子都扔了的之后更新一下。

    然后insert的时候更新一下即可。

    然后维护我们用pushup维护。

    最后,附上丑陋的代码... ...

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define ls ch[p][0]
    #define rs ch[p][1]
    #define get(x) (ch[f[x]][1]==x)
    #define N 100010
    using namespace std;
    typedef long long ll;
    int ch[N][2],oth[N],sum[N],f[N];
    bool rev[N];
    inline bool isroot(int p)
    {
    	// puts("Fuck:isroot");
    	return ch[f[p]][0]!=p&&ch[f[p]][1]!=p;
    }
    inline void pushup(int p)
    {
    	// puts("Fuck:pushup");
    	sum[p]=sum[ls]+sum[rs]+oth[p]+1;
    }
    inline void pushdown(int p)
    {
    	// puts("Fuck:pushdown");
    	if(!rev[p]) return;
    	swap(ch[ls][0],ch[ls][1]); swap(ch[rs][0],ch[rs][1]);
    	rev[ls]^=1; rev[rs]^=1; rev[p]=0;
    }
    void update(int p)
    {
    	// puts("Fuck:update");
    	if(!isroot(p)) update(f[p]);
    	pushdown(p);
    }
    void rotate(int x)
    {
    	// puts("Fuck:rotate");
    	int y=f[x],z=f[y],k=get(x);
    	if(!isroot(y)) ch[z][ch[z][1]==y]=x; 
    	ch[y][k]=ch[x][!k]; f[ch[y][k]]=y;
    	ch[x][!k]=y; f[y]=x; f[x]=z;
    	pushup(y); pushup(x);
    }
    void splay(int x)
    {
    	// puts("Fuck:splay");
    	update(x);
    	for(int t;t=f[x],!isroot(x);rotate(x))
    	{
    		if(!isroot(t)) rotate(get(x)==get(t)?t:x);
    	}
    }
    void access(int p)
    {
    	// puts("Fuck:access");
        int t=0;
        while(p) splay(p),oth[p]+=sum[rs]-sum[t],rs=t,pushup(p),t=p,p=f[p];
    }
    void makeroot(int p)
    {
    	// puts("Fuck:makeroot");
    	access(p); splay(p);
    	swap(ls,rs); rev[p]^=1;
    }
    void link(int x,int y)
    {
    	// puts("Fuck:link");
    	makeroot(x); makeroot(y);
    	f[x]=y; oth[y]+=sum[x]; pushup(y);
    }
    int main()
    {
    	int n,m,x,y;
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++) sum[i]=1;
    	char str[10];
    	while(m--)
    	{
    		scanf("%s%d%d",str,&x,&y);
    		if(str[0]=='A') link(x,y);
    		else makeroot(x),makeroot(y),printf("%lld
    ",(ll)sum[x]*(sum[y]-sum[x]));
    	}
    	return 0;
    }
    /*
    8 6
    
    A 2 3
    
    A 3 4
    
    A 3 8
    
    A 8 7
    
    A 6 5
    
    Q 3 8
    */
    

    小结:如果没听懂可以到JCY的blog中看一看。

  • 相关阅读:
    Delphi stdCall意义
    Delphi 与 DirectX 之 DelphiX(10): TPictureCollectionItem.StretchDraw
    delphi中的TCollection
    Delphi XE5教程8:使用Delphi命名空间
    在 centos 系统中添加审计用户并利用 sudoers 进行权限控制
    在 centos 8 中添加 sudoer 用户
    React.Fragment
    js保留两位小数方法总结
    正则表达式的() [] {} 的区别
    Typora如何配置gitee图床
  • 原文地址:https://www.cnblogs.com/ShuraK/p/9393312.html
Copyright © 2020-2023  润新知