2020 计蒜之道 预赛 第二场 D. 虫洞(困难)(O(n) bfs序+树上差分+树链求并+tarjan求lca)

本文解析了一道关于有根树的题目,介绍了如何利用O(n)复杂度的算法解决查询每个节点在不同深度子树中满足特定条件的子节点数量问题,包括使用BFS、离线tarjan和前缀和等技巧。

题目

简化题意,

n(n<=1e6)个点,1为根的有根树

点i对应一个权值wi,wi在[0,n]之间

对于每个i,问在其子树中有多少种深度d,

满足该深度下存在两个点a、b,使得w_{a} \bigoplus w_{b}=x(x是给出的值,也在[0,n]之间)

对于这个i,计其答案为ans[i],

要求输出\sum_{i=1}^{n}(i\bigoplus (n-ans[i]))对998244353取模的值

思路来源

官方题解

题解

如果n=1e5,可以用unorder_map的双log的启发式合并做,这也是C.虫洞(中等)的做法

启发式合并如果每一层有x个点,就动态开长为x的数组,也可以压到复杂度O(nlogn),

但是常数巨大,所以卡不过D,怎么写怎么T,最后无奈来补O(n)做法


bfs的过程中,一边bfs一边尺取,

tong[x]维护值为x时当前出现的最大的bfs序下标,

能更新当且仅当v所需要的tong[v^x]和v在同一层

区间包含,增序扫r,维护当前最大左端点maxl,让maxl只增不减,即可避免区间嵌套

考虑要对lca(v1,gv1)单点+1,lca(v2,gv2)单点+1,对lca(lca(v1,gv1),lca(v2,gv2))单点-1,

tarjan离线,分两次离线,第一次dfs1把+1的LCA离线出来,第二次dfs2再去离线-1的LCA

最后用dfs3做一遍树上前缀和,顺便统计答案

总复杂度是O(n)的,但是扫了将近10次序列,所以常数还是很大

vector建图怎么改怎么T,链式前向星跑到了600ms上下

代码

#include<bits/stdc++.h>
using namespace std;
namespace fastIO
{
    static char buf[100000],*h=buf,*dp=buf;//缓存开大可减少读入时间,看题目给的空间
    #define gc h==dp&&(dp=(h=buf)+fread(buf,1,100000,stdin),h==dp)?EOF:*h++//不能用fread则换成getchar
    template<typename T>
    inline void read(T&x)
    {
        int f = 1;x = 0;
        register char c(gc);
        while(c>'9'||c<'0'){
            if(c == '-') f = -1;
            c=gc;
        }
        while(c<='9'&&c>='0')x=(x<<1)+(x<<3)+(c^48),c=gc;
        x *= f;
    }
    template<typename T>
    void output(T x)
    {
        if(x<0){putchar('-');x=~(x-1);}
        static int s[20],top=0;
        while(x){s[++top]=x%10;x/=10;}
        if(!top)s[++top]=0;
        while(top)putchar(s[top--]+'0');
    }
}
using namespace fastIO;
#define pb push_back
#define fi first
#define se second
typedef long long ll;
typedef pair<int,int> P;
const int N=1e6+10,mod=998244353;
struct node{
    int lca,pre;
}add[N];
int del[N],c2;
int n,x,v,w[N],c;
int q[N],t,d[N],tong[N];
int ans[N],res;
int head[N],nex[N],to[N],cnt;
int par[N];
bool vis[N],fir[N];
int find(int x){
    return par[x]==x?x:par[x]=find(par[x]);
}
void add_edge(int x,int y){
    nex[++cnt]=head[x];
    to[cnt]=y;
    head[x]=cnt;
}
vector<P>Q[N];
void init(int n){
    for(int i=1;i<=n;++i){
        par[i]=i;
        Q[i].clear();
        vis[i]=0;
    }
}
void unite(int x,int y){
    x=find(x),y=find(y);
    if(x==y)return;
    par[y]=x;
}
void dfs1(int u){
    vis[u]=1;
    for(int i=head[u];i;i=nex[i]){
        int v=to[i];
        dfs1(v);
        unite(u,v);
    }
    for(auto &x:Q[u]){
        int v=x.fi,id=x.se;
        if(!vis[v])continue;
        add[id].lca=find(v);
    }
}
void dfs2(int u){
    vis[u]=1;
    for(int i=head[u];i;i=nex[i]){
        int v=to[i];
        dfs2(v);
        unite(u,v);
    }
    for(auto &x:Q[u]){
        int v=x.fi,id=x.se;
        if(!vis[v])continue;
        del[id]=find(v);
    }
}
void dfs3(int u){
    for(int i=head[u];i;i=nex[i]){
        int v=to[i];
        dfs3(v);
        ans[u]+=ans[v];
    }
    res=(res+(u^(n-ans[u])))%mod;
}
void bfs(int z){
    q[++t]=z;
    d[z]=1;
    int mxl,now,a,v,oth,b;
    for(int s=1;s<=t;++s){
        a=q[s],v=w[a],oth=v^x;
        if(!fir[d[a]]){
            mxl=s-1;
            now=-1;
            fir[d[a]]=1;
        }
        if(tong[oth]>mxl){
            b=q[tong[oth]];
            add[++c]={-1,now};
            Q[a].pb(P(b,c));
            Q[b].pb(P(a,c));
            mxl=tong[oth];
            now=c;
        }
        tong[v]=s;
        for(int i=head[a];i;i=nex[i]){
            int v=to[i];
            if(d[v])continue;
            q[++t]=v;
            d[v]=d[a]+1;
        }
    }
}
int main(){
    read(n),read(x);
    for(int i=2;i<=n;++i){
        read(v);
        add_edge(v,i);
    }
    for(int i=1;i<=n;++i){
        read(w[i]);
        par[i]=i;
    }
    bfs(1);
    dfs1(1);
    init(n);
    for(int i=1;i<=c;++i){
        int lc=add[i].lca,pr=add[i].pre;
        ans[lc]++;
        if(pr!=-1){
            int rc=add[pr].lca;
            ++c2;
            Q[rc].pb(P(lc,c2));
            Q[lc].pb(P(rc,c2));
        }
    }
    dfs2(1);
    for(int i=1;i<=c2;++i){
        ans[del[i]]--;
    }
    dfs3(1);
    printf("%d\n",res);
    return 0;
}
/*
5 0
3 1 5 1
0 0 0 0 0
*/

内容概要:本文围绕列车-轨道-桥梁交互仿真研究,基于Matlab平台构建数值模型,系统分析列车运行过程中轨道与桥梁结构间的动态相互作用机制。研究涵盖多体动力学建模、耦合系统运动方程求解、边界条件设定及仿真结果可视化等关键环节,重点揭示高速行车条件下基础设施的振动传递规律与力学响应特征。该仿真方法可有效评估结构安全性、舒适性指标及疲劳寿命,为轨道交通工程的设计优化与运维管理提供理论支撑和技术路径。文中配套提供了完整的Matlab代码实现方案及操作说明,便于用户复现、验证和拓展相关研究。; 适合人群:具备Matlab编程基础和结构动力学、车辆动力学等相关专业知识的研究生、科研人员及从事铁路工程、桥梁工程与交通系统安全评估的工程技术人才,尤其适合开展轨道交通耦合振动课题的研究者。; 使用场景及目标:①用于高校与科研机构进行列车-轨道-桥梁耦合系统动力学特性的教学演示与科学研究;②支撑高速铁路桥梁的设计优化、运营安全性评估与减振降噪方案验证;③为复杂交通基础设施的多物理场耦合仿真提供建模思路与代码参考。; 阅读建议:建议读者结合所提供的Matlab代码逐模块深入研读,重点关注系统建模假设、质量-刚度-阻尼矩阵构建方法及数值积分算法的实现细节,同时可通过调整参数进行敏感性分析,进一步掌握仿真模型的适用范围与优化方向。
内容概要:本文系统研究了非线性薛定谔方程的物理信息神经网络(PINN)求解方法,提出一种将物理规律嵌入深度学习模型的科学计算新范式。通过构建全连接神经网络架构,将非线性薛定谔方程及其初始/边界条件作为损失函数的核心组成部分,实现了在无须大量标注数据的前提下对复值偏微分方程的高精度数值求解。该方法充分利用自动微分技术精确计算方程残差,有效融合了数据驱动与模型驱动的优势,在光学孤子传播、量子系统演化等典型场景中展现出优异的逼近能力与泛化性能。文中配套提供了完整的Python实现代码,涵盖网络搭建、损失定义、训练优化与结果可视化全流程。; 适合人群:具备Python编程能力与深度学习基础知识,熟悉偏微分方程理论及科学计算的理工科研究生、科研人员,以及从事光学、量子物理、流体力学等领域建模与仿真的工程技术人员。; 使用场景及目标:① 掌握PINN方法的基本原理与实现技巧;② 学习如何将复杂物理方程转化为可训练的神经网络损失项;③ 应用于非线性光学、玻色-爱因斯坦凝聚、水波动力学等问题的仿真与预测;④ 为相关科研课题提供可复现的算法原型与代码参考。; 阅读建议:建议读者结合所提供的Python代码进行动手实践,重点理解神经网络对微分算子的近似机制、损失函数的多任务加权策略以及训练过程中的超参数调优方法,进而可迁移至其他非线性偏微分方程的求解任务,拓展其在交叉学科中的应用边界。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值