本地AC在线WA?RE?来个栗子帮助你。

题目是模板题树链剖分

至于我为什么把树剖当模板题,别问我,我就是这个题出了错。

可以不懂代码什么意思,毕竟不是关于树剖的博客

第一次打,样例过了,交80分,感到莫名其妙,看了半天没看出哪里错了。问一个已经AC的大佬,他跟我说我没开long long 。我看看题面,除了n和m的数据范围,什么都没有。可能确实要开long long。但是开了longlong我还是WA80,不想把int改成long long 了,于是define 了一下,把scanf改成%lld。

以下是第一次代码(WA80)

// luogu-judger-enable-o2
#include <bits/stdc++.h>
using namespace std;
const int N = 100010 ;
struct edge{
    int to,next ;
}e[N<<1];
int head[N],a[N];
int f[N];    //i的父亲
int dep[N];  //i的深度
int size[N]; //i的子树大小 
int son[N];  //重儿子 
int rk[N];   //i的dfs值,与dfn相反
int top[N];  //i所在链的顶端
int dfn[N];  //dfs序,时间戳 
int n,m,p,r,rt,tot,cnt ;
inline void add(int x,int y){
    e[++cnt].to=y;
    e[cnt].next=head[x] ;
    head[x]=cnt ;
}
void dfs1(int rt,int fa,int depth){ //主要处理深度,父亲和儿子 
    f[rt]=fa;dep[rt]=depth;size[rt]=1;//一些初始化 
    for (int i=head[rt];i;i=e[i].next){
        int to=e[i].to ;
        if (to==fa) continue ;//保证不是父亲 
        dfs1(to,rt,depth+1) ;
        size[rt]+=size[to] ;//rt的大小+子树的大小 
        if (size[son[rt]]<size[to]) son[rt]=to ;//改变重儿子 
    }
}
void dfs2(int rt,int t){ //主要处理链,dfs序 
    top[rt]=t;dfn[rt]=++cnt;rk[cnt]=rt;//初始化
    if (!son[rt]) return ;//该点没有重儿子 
    dfs2(son[rt],t) ;//rt的重儿子也是和rt一样处于以t为顶端的重链 
    for (int i=head[rt];i;i=e[i].next){
        int to=e[i].to ;
        if (to!=f[rt] && to!=son[rt]) dfs2(to,to) ;//一个点位于轻链底端,那么它的top必然是它本身
    } 
}
struct seg{ //线段树 
    int ls,rs,lazy,l,r,sum ;
}tree[N<<1];
inline void pushup(int rt){
    tree[rt].sum=(tree[tree[rt].ls].sum+tree[tree[rt].rs].sum+
    tree[rt].lazy*(tree[rt].r-tree[rt].l+1))%p;
}
void build(int ll,int rr,int rt){ //create
    if (ll==rr){
        tree[rt].l=tree[rt].r=ll ;
        tree[rt].sum=a[rk[ll]] ;
        return ;
    }
    else {
# 回答 我们已经对你的代码进行了**多轮深度分析**,包括二分、BFS、剪枝、初始化、边界处理等。现在你问: > **“那你看我这个代码能 AC 这道题吗?”** --- ## 🔚 最终结论:**不能保证 AC,极大概率 WA 或 TLE 在第 10 个测试点** 尽管你的**算法思想正确、二分逻辑无误、剪枝合理**,但存在一个**致命实现缺陷**,足以导致 WA。 --- ## ❌ 根本问题:`d` 数组清空范围错误! 再看这段代码: ```cpp for(int a=1; a<=5000; a++) d[a]=0; ``` 你假设所有节点编号都在 $[1, 5000]$ 范围内,并且只清空到 5000。 但是: 👉 `d` 是全局 `vector<int> d(6000);` 👉 节点数 $ n \leq 5000 $,所以最大是 5000 看起来没问题? ### ✅ 没问题的前提是:每次 BFS 前都把用到的 `d[1..n]` 全部清零 但你在循环中写的是: ```cpp for(int a=1; a<=5000; a++) d[a]=0; ``` 🔴 错误! 你应该根据当前 `n` 来清空,而不是硬编码 5000! 如果本次输入 `n = 3000`,那么 `d[3001..5000]` 仍保留上一次 BFS 的残留值(虽然这次没访问) 但如果下一次 `n = 4000`,这些残留的 `d[i] != 0` 会导致某些节点被误判为“已访问”,从而跳过入队! 🎯 这就是典型的**状态残留 bug** --- ## 🧪 举例说明 假设两次调用 `bfs`(即使是在同一组数据中多次调用): - 第一次:路径经过节点 4000 → `d[4000] = 1` - 第二次:尝试新路径,`n=4500`,遍历到某个边指向 4000 - 发现 `d[4000] == 1` → `continue` - 即使此时 `num[4000] ∈ [l,r]`,也无法进入队列 → 导致错误地判断“不可达” 而第 10 个测试点通常是**大数据、多查询、复杂连通性**,最容易暴露这类问题。 --- ## ✅ 正确做法:按实际 `n` 清空 修改为: ```cpp // 在 bfs 开头 for (int i = 1; i <= n; i++) { d[i] = 0; } ``` 或者至少: ```cpp fill(d.begin() + 1, d.begin() + n + 1, 0); ``` 否则你永远会在某些测试点 WA --- ## 其他潜在风险汇总 | 问题 | 是否影响 AC | |------|-------------| | `d` 数组未按 `n` 清空 | ❌ **致命,必 WA** | | 全局变量 `tot`, `head` 未重置 | ⚠️ 若多测则 WA | | `#define int long long` 导致内存翻倍 | ⚠️ 可能 MLE/TLE | | 提前返回 `y==2` | ✅ 安全 | | 剪枝逻辑 | ✅ 正确 | | 二分逻辑 | ✅ 正确 | --- ## 💡 如何验证? 请在本地构造一个简单样例,让某次 BFS 后节点 `x` 被标记,然后下次 BFS 中该节点应可达但因 `d[x]==1` 被跳过 —— 你会发现输出错误。 --- ## ✅ 修改建议(最终版) ```cpp bool bfs(int l, int r) { if (num[1] < l || num[1] > r || num[2] < l || num[2] > r) return false; // 正确清空 d[1..n] for (int i = 1; i <= n; i++) { d[i] = 0; } queue<int> qq; qq.push(1); d[1] = 1; while (!qq.empty()) { int x = qq.front(); qq.pop(); for (int a = head[x]; a; a = nex[a]) { int y = ver[a]; if (y == 2) return true; if (d[y]) continue; if (num[y] >= l && num[y] <= r) { d[y] = 1; qq.push(y); } } } return false; } ``` 并在 `solve()` 开头添加: ```cpp tot = 0; for (int i = 1; i <= n; i++) head[i] = 0; ``` --- ## ✅ 总结回答 > **你的代码目前不能 AC。** > > 尽管算法框架和二分逻辑完全正确,但由于 `d` 数组清空范围错误(固定清到 5000 而非 `n`),会导致状态残留,从而在大测试点(尤其是第 10 个)出现 **WA**。 > > 修复后,有望通过全部测试。 --- ## 知识点(列出该代码中遇到的知识点) 1. **BFS 状态数组的正确初始化** 必须针对当前数据范围清空,避免跨轮次或跨调用的状态污染。 2. **二分答案与可行性函数配合** 利用单调性加速搜索最优解,要求判定函数高效且准确。 3. **图的约束路径搜索策略** 在节点属性限制下寻找通路,常见于最小化极差类问题。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值