zoukankan      html  css  js  c++  java
  • 3.28 省选模拟赛 染色 LCT+线段树

    avatar
    avatar

    发现和SDOI2017树点涂色差不多 但是当时这道题模拟赛的时候不会写 赛后也没及时订正 所以这场模拟赛的这道题虽然秒想到了LCT和线段树但是最终还是只是打了暴力。

    痛定思痛 还是要把这道题给补了。

    但是对于这道题来说 暴力还是有价值的。

    考虑20分 每次暴力dfs.

    考虑对于树是随机生成的 那么期望高度为logn 我们发现每次修改只用修改到1 也就是说每次暴力修改颜色的话只需要logn的时间复杂度.

    考虑如何动态维护子树内的值 考虑修改一个点的颜色 子树内之前和它颜色一样的点 显然子树内部整体答案+1 如果不一样那么没有影响 和当前一样也没有影响。

    考虑这个点和它的父亲此时答案是一样的如果原来答案也是一样的 那么没有任何的修改 如果不一样 那么子树内部整体-1.

    依靠这个思路我们可以 维护一棵线段树 logn的时间内进行区间修改 区间查询。

    考虑100分的做法 发现我们暴力慢的地方在于每次都要向上跳。

    有优化的地方是 如果当前点可能树上的一段区间颜色是一样的 我们只需要在 当前修改节点x和那段颜色一样的点y的LCA处修改 剩下的直接向上跳即可。

    换个角度 其实这个染颜色其实像是LCT 的ACCESS操作 这样我们就可以很方便的维护上述的操作。

    考虑这样做的向上跳的复杂度 可以发现利用LCT的性质 均摊logn.

    所以每次在access的时候 完成子树内部的修改即可。维护dfs序线段树 复杂度nlog^2.

    const int MAXN=150010;
    int n,Q,len,cnt;
    int lin[MAXN],c[MAXN][2],f[MAXN],ver[MAXN<<1],nex[MAXN<<1],dfn[MAXN],out[MAXN];
    int fa[MAXN],d[MAXN],sz[MAXN],son[MAXN],top[MAXN],v[MAXN];
    inline void add(int x,int y)
    {
    	ver[++len]=y;
    	nex[len]=lin[x];
    	lin[x]=len;
    }
    inline void dfs(int x)
    {
    	sz[x]=1;
    	go(x)if(tn^fa[x])
    	{
    		fa[tn]=x;d[tn]=d[x]+1;
    		dfs(tn);
    		sz[x]+=sz[tn];
    		if(sz[tn]>sz[son[x]])son[x]=tn;
    	}
    }
    inline void dfs(int x,int father)
    {
    	top[x]=father;dfn[x]=++cnt;v[cnt]=x;
    	if(son[x])dfs(son[x],father);
    	go(x)if(tn!=fa[x]&&tn!=son[x])dfs(tn,tn);
    	out[x]=cnt;
    }
    inline int LCA(int x,int y)
    {
    	while(top[x]^top[y])
    	{
    		if(d[top[x]]<d[top[y]])swap(x,y);
    		x=fa[top[x]];
    	}
    	return d[x]<d[y]?x:y;
    }
    struct seg{int tag,mx,l,r;ll sum;}t[MAXN<<2];
    inline void spread(int p,int v)
    {
    	tag(p)+=v;mx(p)+=v;
    	sum(p)+=(r(p)-l(p)+1)*v;
    }
    inline void pushdown(int p)
    {
    	spread(zz,tag(p));
    	spread(yy,tag(p));
    	tag(p)=0;
    }
    inline void pushup(int p)
    {
    	mx(p)=max(mx(zz),mx(yy));
    	sum(p)=sum(zz)+sum(yy);
    }
    inline void build(int p,int l,int r)
    {
    	l(p)=l;r(p)=r;
    	if(l==r){mx(p)=d[v[l]];sum(p)=d[v[l]];return;}
    	int mid=(l+r)>>1;
    	build(zz,l,mid);build(yy,mid+1,r);
    	pushup(p);
    }
    inline void change(int p,int l,int r,int x)
    {
    	if(l<=l(p)&&r>=r(p)){spread(p,x);return;}
    	int mid=(l(p)+r(p))>>1;
    	if(tag(p))pushdown(p);
    	if(l<=mid)change(zz,l,r,x);
    	if(r>mid)change(yy,l,r,x);
    	pushup(p);
    }
    inline int ask(int p,int x)
    {
    	if(l(p)==r(p))return mx(p);
    	int mid=(l(p)+r(p))>>1;
    	if(tag(p))pushdown(p);
    	if(x<=mid)return ask(zz,x);
    	return ask(yy,x);
    }
    inline int ask(int p,int l,int r)
    {
    	if(l<=l(p)&&r>=r(p))return mx(p);
    	int mid=(l(p)+r(p))>>1,w=0;
    	if(tag(p))pushdown(p);
    	if(l<=mid)w=ask(zz,l,r);
    	if(r>mid)w=max(w,ask(yy,l,r));
    	return w;
    }
    inline ll asksum(int p,int l,int r)
    {
    	if(l<=l(p)&&r>=r(p))return sum(p);
    	int mid=(l(p)+r(p))>>1;ll w=0;
    	if(tag(p))pushdown(p);
    	if(l<=mid)w=asksum(zz,l,r);
    	if(r>mid)w=w+asksum(yy,l,r);
    	return w;
    }
    inline void asksum(int x)
    {
    	ll w=asksum(1,dfn[x],out[x]);
    	double ans=1.0*w/sz[x];
    	printf("%.10lf
    ",ans);
    }
    inline int pd(int x){return c[f[x]][1]==x||c[f[x]][0]==x;}//判断x是否为根.
    inline void rotate(int x)
    {
    	int old=f[x],oldf=f[old],k=c[old][1]==x;
    	c[old][k]=c[x][k^1];c[x][k^1]=old;
    	if(pd(old))c[oldf][c[oldf][1]==old]=x;
    	if(c[old][k])f[c[old][k]]=old;
    	f[old]=x;f[x]=oldf;
    }
    inline void splay(int x)
    {
    	while(pd(x))
    	{
    		if(pd(f[x]))rotate((c[f[x]][1]==x)^(c[f[f[x]]][1]==f[x])?x:f[x]);
    		rotate(x);
    	}
    }
    inline int findroot(int x)
    {
    	splay(x);
    	while(c[x][0])x=c[x][0];
    	splay(x);return x;
    }
    inline void access(int x)
    {
    	int y=0;
    	while(x)
    	{
    		splay(x);
    		if(c[x][1])
    		{
    			int w=c[x][1];
    			c[x][1]=0;
    			w=findroot(w);
    			change(1,dfn[w],out[w],1);
    		}
    		if(y)
    		{
    			y=findroot(y);
    			change(1,dfn[y],out[y],-1);
    		}
    		c[x][1]=y;
    		y=x;x=f[x];
    	}
    }
    int main()
    {
    	freopen("1.in","r",stdin);
    	//freopen("1.out","w",stdout);
    	get(n);
    	rep(2,n,i)
    	{
    		int x,y;
    		get(x)+1;get(y)+1;
    		//cout<<x<<' '<<y<<endl;
    		add(x,y);add(y,x);
    	}
    	get(Q);dfs(1);dfs(1,1);
    	build(1,1,n);
    	rep(1,n,i)f[i]=fa[i];
    	rep(1,Q,i)
    	{
    		char ch=getc();
    		while(ch!='q'&&ch!='O')ch=getc();
    		int get(x)+1;
    		if(ch=='O')access(x);
    		else asksum(x);
    	}
    	return 0;
    }
    
  • 相关阅读:
    tomcat请求流程浅解
    jdk8为啥lambda表达式建议你用冒号形式调用方法
    打印目录树形结构
    类斐波那契数列的java实现
    sping boot 如何将外部引入的jar包打到fat jar里面
    java多线程之生产者消费者
    Hadoop、Hbase、ZooKeeper的搭建
    java 静态代码块、构造代码块、构造函数调用顺序
    MyBatis的 or 和and 问题
    mysql
  • 原文地址:https://www.cnblogs.com/chdy/p/12669885.html
Copyright © 2011-2022 走看看