uoj#84. 【UR #7】水题走四方
题目描述
n<=5*10^6
题解
好题
直接贪心/dp是假的,反例考虑两条长链+上面的一些短链
硬点本体只会往下走,分身负责清理掉伸出去的链,最后留下一条最长链一起走下去
dp方程式见官方题解,直接做是n^2的
一些性质:
①留下的链一定在本体所在点上,否则可以再分一段
②转移过来的点之间的距离要不大于留下的链长,否则可以再分一段
然后暴力维护不同长链深度的即可O(n√n),因为只有√n种
更强的性质:
只需要从最近的祖先且除当前点所在子树的最长链深度>=当前点的点转移+当前点的父亲即可
证明考虑如果有两个满足的点uv,其中u在v上方,则从u->当前点和u->v->当前点所一起走的路程一样,并且后者其余叶子到他的距离减少了
当前点的父亲是因为性质②要传递
具体实现长链剖分,先走轻儿子并把重儿子的深度加上去,再走重儿子并把轻儿子的深度加上去
超过了范围就直接弹栈,正确性证明:
一个点一定能被最近且满足条件的点更新(最近的点不会被弹掉),如果两点之间没有分支则显然可以
否则如果当前点在分支点的重子树里,由于前提条件存在所以轻子树的深度不会超过当前点的深度,更不会超过转移点的深度,所以走下去不会弹掉,如果在轻子树里就不满足前提条件
UPD:不知道上面的sb在讲什么,所以再写一遍
如果一个点v的转移点是u,那么可以发现u->v都是重边,否则在轻边处会有新的u
对于u->v中间的一个点x,x在遍历的时候先走轻边,由于有重儿子保护所以u不会被弹掉
所以u会一直保留直到某棵树里最浅点深度大于它,这时弹掉就不会错了
时间复杂度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\n",ans);
fclose(stdin);
fclose(stdout);
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 现代计算机视觉入门之:什么是图片特征编码
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· Spring AI + Ollama 实现 deepseek-r1 的API服务和调用
· 数据库服务器 SQL Server 版本升级公告
· 程序员常用高效实用工具推荐,办公效率提升利器!
· C#/.NET/.NET Core技术前沿周刊 | 第 23 期(2025年1.20-1.26)