P4338 [ZJOI2018]历史 P3703 [SDOI2017]树点涂色 题解

P4338 [ZJOI2018]历史 P3703 [SDOI2017]树点涂色 题解

将这两题放在一起,因为他们有十分显然的共同之处。

题意:

[ZJOI2018]历史 给出一棵树,给定每一个点的access次数,计算轻重链切换次数的最大值,access次数每次询问单点修改。

[SDOI2017]树点涂色 给一棵树,询问可以将\(x\)\(1\)都修改为一个新颜色;定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色;询问一条路径的权值;询问子树内哪个节点到\(1\)的权值最大。

[ZJOI2018]历史

我们考虑一次从下往上的access,途中会遇到很多次轻链。我们定义一个点的答案为这个点作为轻链顶端经过所有操作后其下轻链总共被切换的次数。

那么这个点的答案仅仅和其子树内的节点的access情况有关。

很容易发现,当连续(指操作序列去掉非子树内的节点后)两次access来自不同的孩子节点所在的子树(或者是一次来自孩子节点的子树另一次来自这个节点自己),那么就会产生1的贡献。所以既然要使得最后的答案最大,那么我们要交错排开各个孩子节点子树和子树根节点之间的access操作。

一个不难想到的结论:如果点在不同的子树内分布均匀,那么铁定是可以做到相邻两两不同的。所以造成存在相邻相同的只会有一种可能:某一种颜色的个数比剩余颜色个数+1还多。这提醒我们关注孩子节点子树和自己本身的最大值。

也就是说,这个式子呼之欲出:每个节点的答案为\(\min\{2*(t-h),t-1\}\),其中\(t\)为子树内所有节点的access次数总和,\(h\)为孩子节点子树access次数总和和子树根节点本身access次数的最大值。

至此解决了只是单次无修改的询问的答案。

接下来考虑修改了一个点有什么影响。由于\(t\)是很好维护的,所以我们关注这个\(h\)。由于\(h\)除了自己节点也算在内之外就是个子树\(size\)的最大值,这个性质和轻重链剖分是一样的,所以我们也很好得到这样一个结论:一个点到\(1\)的路径中只有\(O(\log n)\)个节点是轻链。也就是说,轻链是维护的时间复杂度是很低的。另一方面,这告诉我们重链只会越来越重,而且是求\(\min\)导致重链对答案没有影响;轻链会改变答案。所以改变答案也只有\(O(\log n)\)次。

我们只要用LCT或树链剖分维护一下轻重链即可。

[SDOI2017]树点涂色

有了上面这一题,这题就迎刃而解了。

每次修改就是access,然后每个点的到根的路径权值就是其到根经过的轻链的条数,而这个显然是可以把x到y的路径拆成两个到根的路径权值减去lca到根的权值的。用线段树在access转换轻重链时维护一下即可。

[ZJOI2018]历史 代码:

#include <bits/stdc++.h>
using namespace std;
const int S=400004;
int n,m,h[S],nx[S<<1],v[S<<1],eg=1;
long long a[S];
inline void egadd(int uu,int vv)
{
	nx[++eg]=h[uu];h[uu]=eg;
	v[eg]=vv;
}
long long res=0;
struct LCT
{
	int ch[S][2],fa[S];
	long long s[S],si[S];
	inline bool isroot(int x){return ch[fa[x]][0]!=x && ch[fa[x]][1]!=x;}
	inline bool lor(int x){return ch[fa[x]][1]==x;}
	inline void upd(int x){s[x]=s[ch[x][0]]+s[ch[x][1]]+si[x]+a[x];}
	inline void rtt(int x)
	{
		int y=fa[x],r=fa[y];
		int yo=lor(x),w=ch[x][yo^1];
		if (!isroot(y)) ch[r][lor(y)]=x;ch[x][yo^1]=y;ch[y][yo]=w;
		if (w) fa[w]=y;fa[y]=x;fa[x]=r;
		upd(y);upd(x);
	}
	inline void splay(int x)
	{
		while (!isroot(x))
		{
			if (isroot(fa[x])) rtt(x);
			else if (lor(fa[x])==lor(x)) rtt(fa[x]),rtt(x);
			else rtt(x),rtt(x);
		}
	}
	inline long long calc(int x,long long t,long long h)
	{
		if (ch[x][1]) return (t-h)*2;
		else if (a[x]*2>=t+1) return (t-a[x])*2;
		else return t-1;
	}
	inline void access(int x,int w)
	{
		long long t,h;int y=0;
		splay(x);
		h=s[ch[x][1]];t=s[x]-s[ch[x][0]];
		res-=calc(x,t,h);s[x]+=w;a[x]+=w;t+=w;
		if (h*2<t+1) si[x]+=h,ch[x][1]=0;
		res+=calc(x,t,h);upd(x);
		for (x=fa[y=x];x;x=fa[y=x])
		{
			splay(x);
			h=s[ch[x][1]];t=s[x]-s[ch[x][0]];
			res-=calc(x,t,h);s[x]+=w;si[x]+=w;t+=w;
			if (h*2<t+1) si[x]+=h,ch[x][1]=0,h=0;
			if (s[y]*2>t) si[x]-=s[y],ch[x][1]=y,h=s[y];
			res+=calc(x,t,h);upd(x);
		}
	}
	inline void dfs(int x)
	{
		s[x]=a[x];
		int p=0;
		long long mx=a[x];
		for (int i=h[x];i;i=nx[i])
			if (v[i]!=fa[x])
			{
				fa[v[i]]=x;
				dfs(v[i]);
				s[x]+=s[v[i]];
				if (mx<s[v[i]]) mx=s[p=v[i]];
			}
		res+=min(s[x]-1,2*(s[x]-mx));
		if (mx*2>=s[x]+1) ch[x][1]=p;
		si[x]=s[x]-a[x]-s[ch[x][1]];
	}
}t;
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("P4338.in","r",stdin);
//	freopen(".out","w",stdout);
	#endif
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;++i)
		scanf("%lld",a+i);
	for (int i=1;i<n;++i)
	{
		int uu,vv;scanf("%d%d",&uu,&vv);
		egadd(uu,vv);
		egadd(vv,uu);
	}
	t.dfs(1);
	printf("%lld\n",res);
	int x,w;
	while (m--)
	{
		scanf("%d%d",&x,&w);
		t.access(x,w);
		printf("%lld\n",res);
	}
	return 0;
}

[SDOI2017]树点涂色

#include <cstdio>
#include <utility>
using namespace std;
const int S=100050;
int n,h[S],nx[S<<1],v[S<<1],m,eg=1,f[S];
int ch[S][2],fat[S];
int sg[S<<2],add[S<<2];
int dep[S],s[S],e[S],t[S],_=0,fa[S],rk[S];
#define cd (w<<1)
inline void down(int w)
{
    if (add[w])
    {
        sg[cd]+=add[w];
        sg[cd|1]+=add[w];
        add[cd]+=add[w];
        add[cd|1]+=add[w];
        add[w]=0;
    }
}
inline int ma(int a,int b){return a>b?a:b;}
void sgb(int l,int r,int w)
{
    if (l==r) {sg[w]=dep[rk[l]]+1;return;}
    int mid=(l+r)>>1;
    sgb(l,mid,cd);sgb(mid+1,r,cd|1);
    sg[w]=ma(sg[cd],sg[cd|1]);
}
inline void sgadd(int l,int r,int ll,int rr,int k,int w)
{
    if (ll<=l && r<=rr)
    {
        sg[w]+=k;
        add[w]+=k;
        return;
    }
    int mid=(l+r)>>1;
    down(w);
    if (ll<=mid) sgadd(l,mid,ll,rr,k,cd);
    if (rr>mid) sgadd(mid+1,r,ll,rr,k,cd|1);
    sg[w]=ma(sg[cd],sg[cd|1]);
}
inline int sgcal(int l,int r,int k,int w)
{
    if (l==r) return sg[w];
    int mid=(l+r)>>1;
    down(w);
    if (k<=mid) return sgcal(l,mid,k,cd);
    else return sgcal(mid+1,r,k,cd|1);
}
inline int sgid(int l,int r,int ll,int rr,int w)
{
    if (ll<=l && r<=rr) return sg[w];
    int mid=(l+r)>>1;
    down(w);
    if (rr<=mid) return sgid(l,mid,ll,rr,cd);
    else if (ll>mid) return sgid(mid+1,r,ll,rr,cd|1);
    else return ma(sgid(l,mid,ll,rr,cd),sgid(mid+1,r,ll,rr,cd|1));
}
inline bool lor(int x){return ch[fat[x]][1]==x;}
inline bool isroot(int x){return ch[fat[x]][0]!=x && ch[fat[x]][1]!=x;}
inline void rtt(int x)
{
    int y=fat[x],r=fat[y];
    int yo=lor(x),w=ch[x][yo^1];
    if (!isroot(y)) ch[r][lor(y)]=x;ch[x][yo^1]=y;ch[y][yo]=w;
    if (w) fat[w]=y;fat[y]=x;fat[x]=r;
}
inline void splay(int x)
{
    if (!x) return;
    while (!isroot(x))
    {
        if (isroot(fat[x])) rtt(x);
        else if (lor(fat[x])==lor(x)) rtt(fat[x]),rtt(x);
        else rtt(x),rtt(x);
    }
}
inline int find_mi(int x){while (ch[x][0]) x=ch[x][0];return x;}
inline void access(int x)
{
    for (int y=0,o1,o2;x;x=fat[y=x])
    {
        splay(x);o1=0;
        if (ch[x][1]) o1=find_mi(ch[x][1]),sgadd(1,n,f[o1],f[o1]+s[o1]-1,1,1);
        o2=0;
        if (y) o2=find_mi(y),splay(o2),sgadd(1,n,f[o2],f[o2]+s[o2]-1,-1,1);
        ch[x][1]=o2;splay(o1);
    }
}
inline void egadd(int uu,int vv)
{
    nx[++eg]=h[uu];h[uu]=eg;
    v[eg]=vv;
}
int q[S];
void dfs_1()
{
    int fr=1,tp=1;q[1]=1;
    while (fr<=tp)
    {
        int x=q[fr++];
        for (int i=h[x];i;i=nx[i])
            if (v[i]!=fa[x])
            {
                fa[v[i]]=x;
                dep[v[i]]=dep[x]+1;
                q[++tp]=v[i];
            }	
    }
    for (int i=n,x;i>=1;--i)
    {
        x=q[i];s[x]=1;
        for (int i=h[x];i;i=nx[i])
            if (v[i]!=fa[x])
            {
                s[x]+=s[v[i]];
                if (s[v[i]]>s[e[x]])
                    e[x]=v[i];
            }	
    }
}
void dfs_2()
{
    int fr=1,tp=1;q[1]=1;
    while (fr<=tp)
    {
        int x=q[tp--];int top=x;f[x]=++_;t[x]=top;rk[_]=x;
        while (e[x])
        {
            for (int i=h[x];i;i=nx[i])
                if (v[i]!=fa[x] && v[i]!=e[x])
                    q[++tp]=v[i];
            x=e[x];f[x]=++_;t[x]=top;rk[_]=x;
        }
    }
}
inline int trlca(int x,int y)
{
    while (t[x]!=t[y])
    {
        if (dep[t[x]]<dep[t[y]])
            x^=y^=x^=y;
        x=fa[t[x]];
    }
    if (dep[x]<dep[y]) return x;
    else return y;
}
inline void read(int &s)
{
    s=0;char c=getchar();
    while (c<'0' || c>'9') c=getchar();
    while (c>='0' && c<='9') s=s*10+(c^48),c=getchar();
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<n;i++)
    {
        int uu,vv;read(uu);read(vv);
        egadd(uu,vv);egadd(vv,uu);
    }
    dfs_1();
    dfs_2();
    for (int i=1;i<=n;++i)
        fat[i]=fa[i];
    sgb(1,n,1);
    int op,x,y;
    while (m--)
    {
        read(op);read(x);
        if (op==1) access(x);
        else if (op==2)
        {
            read(y);
            printf("%d\n",sgcal(1,n,f[x],1)+sgcal(1,n,f[y],1)-2*sgcal(1,n,f[trlca(x,y)],1)+1);
        }
        else printf("%d\n",sgid(1,n,f[x],f[x]+s[x]-1,1));
    }
    return 0;
}
posted @ 2020-06-29 21:56  Hygebra  阅读(235)  评论(0编辑  收藏  举报