【XSY2665】没有上司的舞会 LCT DP

题目大意

  有一棵树,最开始只有一个点。每次会往这棵树中加一个点,总共(n)次。输出每次加点后树的最大独立集大小。

  强制在线。

  (nleq 300000)

题解

  显然是LCT。

  那么要维护什么呢?

  先看看DP方程:设(f_{i,0})为以(i)为根的子树中(i)这个点不选的答案,(f_{i,1})(i)这个点选的答案。显然

[egin{align} f_{i,0}&=sum_{v}max(f_{v,0},f_{v,1})\ f_{i,1}&=1+sum_v f_{v,0} end{align} ]

  先看看一条链要怎么做。设(s_{i,j})为某一段中第一个点的状态为(i),在后面补一个状态为(j)的点时这一段的贡献。这个东西很容易合并。

  只有一个点时

[egin{align} s_{0,0}&=0\ s_{0,1}&=0\ s_{1,0}&=1\ s_{1,1}&=-infty end{align} ]

  那树上要怎么做?

  容易观察到(i)的各个儿子之间是互不影响的。可以像这道题一样,把整棵树剖成轻重链,每个点的贡献要加上这个点的轻儿子的贡献。

  access和link时处理一下即可。

  时间复杂度:(O(nlog n))

题解

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<cmath>
#include<functional>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
void sort(int &a,int &b)
{
	if(a>b)
		swap(a,b);
}
void open(const char *s)
{
#ifndef ONLINE_JUDGE
	char str[100];
	sprintf(str,"%s.in",s);
	freopen(str,"r",stdin);
	sprintf(str,"%s.out",s);
	freopen(str,"w",stdout);
#endif
}
int rd()
{
	int s=0,c;
	while((c=getchar())<'0'||c>'9');
	do
	{
		s=s*10+c-'0';
	}
	while((c=getchar())>='0'&&c<='9');
	return s;
}
void put(int x)
{
	if(!x)
	{
		putchar('0');
		return;
	}
	static int c[20];
	int t=0;
	while(x)
	{
		c[++t]=x%10;
		x/=10;
	}
	while(t)
		putchar(c[t--]+'0');
}
int upmin(int &a,int b)
{
	if(b<a)
	{
		a=b;
		return 1;
	}
	return 0;
}
int upmax(int &a,int b)
{
	if(b>a)
	{
		a=b;
		return 1;
	}
	return 0;
}
struct p
{
	ll s11,s12,s21,s22;
	p()
	{
		s11=s12=s21=s22=0;
	}
};
p merge(p a,p b)
{
	p c;
	c.s11=max(a.s11+b.s11,a.s12+b.s21);
	c.s12=max(a.s11+b.s12,a.s12+b.s22);
	c.s21=max(a.s21+b.s11,a.s22+b.s21);
	c.s22=max(a.s21+b.s12,a.s22+b.s22);
	return c;
}
void add(p &a,ll s11,ll s12,ll s21,ll s22)
{
	a.s11+=s11;
	a.s12+=s12;
	a.s21+=s21;
	a.s22+=s22;
}
namespace lct
{
	int f[300010];
	int a[300010][2];
	p v[300010];
	p s[300010];
	int root(int x)
	{
		return !f[x]||(a[f[x]][0]!=x&&a[f[x]][1]!=x);
	}
	void mt(int x)
	{
		s[x]=v[x];
		if(a[x][0])
			s[x]=merge(s[a[x][0]],s[x]);
		if(a[x][1])
			s[x]=merge(s[x],s[a[x][1]]);
	}
	void rotate(int x)
	{
		int p=f[x];
		int q=f[p];
		int ps=(x==a[p][1]);
		int qs=(p==a[q][1]);
		int ch=a[x][ps^1];
		if(!root(p))
			a[q][qs]=x;
		a[x][ps^1]=p;
		a[p][ps]=ch;
		if(ch)
			f[ch]=p;
		f[p]=x;
		f[x]=q;
		mt(p);
	}
	void splay(int x)
	{
		while(!root(x))
		{
			int p=f[x];
			if(!root(p))
			{
				int q=f[p];
				if((p==a[q][1])==(x==a[p][1]))
					rotate(p);
				else
					rotate(x);
			}
			rotate(x);
		}
		mt(x);
	}
	void access(int x)
	{
		int y=x;
		int t=0;
		while(x)
		{
			splay(x);
			add(v[x],max(s[a[x][1]].s21,s[a[x][1]].s22),max(s[a[x][1]].s21,s[a[x][1]].s22),max(max(s[a[x][1]].s11,s[a[x][1]].s12),max(s[a[x][1]].s21,s[a[x][1]].s22)),max(max(s[a[x][1]].s11,s[a[x][1]].s12),max(s[a[x][1]].s21,s[a[x][1]].s22)));
			add(v[x],-max(s[t].s21,s[t].s22),-max(s[t].s21,s[t].s22),-max(max(s[t].s11,s[t].s12),max(s[t].s21,s[t].s22)),-max(max(s[t].s11,s[t].s12),max(s[t].s21,s[t].s22)));
			a[x][1]=t;
			mt(x);
			t=x;
			x=f[x];
		}
		splay(y);
	}
	void link(int x,int y)
	{
		v[x].s11=-0x7fffffff;
		v[x].s12=1;
		v[x].s21=0;
		v[x].s22=0;
		mt(x);
		access(y);
		f[x]=y;
		a[y][1]=x;
//		add(v[y],1,0);
		mt(y);
	}
};
int main()
{
	open("b");
	int n,type;
	scanf("%d%d",&n,&type);
	int i,x;
	int ans=0;
	lct::v[1].s11=-0x7fffffff;
	lct::v[1].s12=1;
	lct::v[1].s21=0;
	lct::v[1].s22=0;
	lct::mt(1);
	for(i=2;i<=n+1;i++)
	{
		scanf("%d",&x);
		if(type)
			x^=ans;
		x++;
		lct::link(i,x);
		lct::access(1);
		ans=max(max(lct::s[1].s11,lct::s[1].s21),max(lct::s[1].s12,lct::s[1].s22));
		printf("%d
",ans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/ywwyww/p/8513335.html