题目描述
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;
}