uoj#84. 【UR #7】水题走四方 题目描述 题解 code

uoj#84. 【UR #7】水题走四方
题目描述
题解
code

n<=5*10^6

题解

好题

直接贪心/dp是假的,反例考虑两条长链+上面的一些短链

硬点本体只会往下走,分身负责清理掉伸出去的链,最后留下一条最长链一起走下去

dp方程式见官方题解,直接做是n^2的

一些性质:

①留下的链一定在本体所在点上,否则可以再分一段

②转移过来的点之间的距离要不大于留下的链长,否则可以再分一段

然后暴力维护不同长链深度的即可O(n√n),因为只有√n种

更强的性质:

只需要从最近的祖先且除当前点所在子树的最长链深度>=当前点的点转移+当前点的父亲即可

证明考虑如果有两个满足的点uv,其中u在v上方,则从u->当前点和u->v->当前点所一起走的路程一样,并且后者其余叶子到他的距离减少了

当前点的父亲是因为性质②要传递

具体实现长链剖分,先走轻儿子并把重儿子的深度加上去,再走重儿子并把轻儿子的深度加上去

超过了范围就直接弹栈,正确性证明:

一个点一定能被最近且满足条件的点更新(最近的点不会被弹掉),如果两点之间没有分支则显然可以

否则如果当前点在分支点的重子树里,由于前提条件存在所以轻子树的深度不会超过当前点的深度,更不会超过转移点的深度,所以走下去不会弹掉,如果在轻子树里就不满足前提条件

时间复杂度O(n)

code

#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
#define ll long long
//#define file
using namespace std;

int a[5000001][2],ls[5000001],fa[5000001],p[5000001],P[5000001][2],d[5000001],D[5000001],nx[5000001],n,i,j,k,l,len,tot;
ll size[5000001],sum[5000001],f[5000001],dp[5000001],ans;
char ch;

void New(int x,int y) {++len;a[len][0]=y;a[len][1]=ls[x];ls[x]=len;}
void dfs(int t)
{
	int i;
	if (!ls[t]) size[t]=1;
	
	for (i=ls[t]; i; i=a[i][1])
	{
		dp[a[i][0]]=dp[t]+1,dfs(a[i][0]);
		size[t]+=size[a[i][0]];
		sum[t]+=sum[a[i][0]]+size[a[i][0]];
		
		if (d[t]<d[a[i][0]]+1) D[t]=d[t],d[t]=d[a[i][0]]+1,nx[t]=a[i][0];
		else D[t]=max(D[t],d[a[i][0]]+1);
	}
}
void Dfs(int Fa,int t)
{
	int i;
	while (tot && dp[P[tot][0]]+P[tot][1]<dp[t]) --tot;
	if (t>1)
	{
		if (tot)
		f[t]=f[P[tot][0]]+(sum[P[tot][0]]-sum[t]-size[t]*(dp[t]-dp[P[tot][0]]));
		else
		f[t]=f[Fa]+1;
		f[t]=min(f[t],f[Fa]+(sum[Fa]-sum[t]-size[t]*(dp[t]-dp[Fa]))+(!D[Fa]));
	}
	
	if (!nx[t])
	return;
	
	++tot;P[tot][0]=t;P[tot][1]=d[t];
	for (i=ls[t]; i; i=a[i][1])
	if (a[i][0]!=nx[t])
	Dfs(t,a[i][0]);
	if (tot && P[tot][0]==t) --tot;
	
	if (nx[t])
	{
		++tot;P[tot][0]=t;P[tot][1]=D[t];
		Dfs(t,nx[t]);
		if (tot && P[tot][0]==t) --tot;
	}
}

int main()
{
	#ifdef file
	freopen("uoj84.in","r",stdin);
	#endif
//	freopen("b.out","w",stdout);
	
	scanf("%d",&n);j=k=0;
	fo(i,1,n+n)
	{
		ch=getchar();
		while (ch!='(' && ch!=')') ch=getchar();
		
		if (ch=='(') fa[++k]=p[j],p[++j]=k;
		else --j;
	}
	fo(i,2,n) New(fa[i],i);
	
	dfs(1);
	Dfs(0,1);
	
	ans=9223372036854775807ll;
	fo(i,1,n) if (!nx[i]) ans=min(ans,f[i]);
	printf("%lld
",ans);
	
	fclose(stdin);
	fclose(stdout);
	return 0;
}