bzoj 3626: [LNOI2014]LCA

【题意】


【题解】

开始做这道题的时候抓住 lca 不放, 想了一些不太优美的做法 。

优美的正解:

考虑 与 z 的 lca 是 x 的点: 这时候 z 一定是 x 的一个后代, 而这些点 也都是 x 的一个后代 且 与 z 不属于 一个 x 的子节点 的枝 上。

x 这个点对 答案的 贡献就是 depth[x] * 上述点的个数。

显然“上述点” 的个数 应该是 sum(fat[x]) - sum(x),而这个数如果一直 加到根的话 是可以消掉的,,还是挺显然的,,

然后…… 这不是煞笔题么, 就是维护一个子树的大小啊。

把询问离线, 然后从 1 到 n 把点加进去, 每加进去一个点 就把 从 它 到 根 这一条路上的所有点 的子树大小都加上一, 裸树剖。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#define MAXN 50005
#define mod 201314
using namespace std;
int aa[MAXN], cnt, n, q, ee, head[MAXN], dep[MAXN], sz[MAXN], son[MAXN], top[MAXN], ti[MAXN], fat[MAXN], ans[MAXN], lala;
struct Tree{int l, r, tag, sum;}tree[MAXN * 4];
struct Edge{int to, next;}edge[MAXN * 2];
struct La{int wei, id, flag, z;}la[MAXN * 2];
bool operator < (La a, La b){return a.wei < b.wei;}
inline void addedge(int x, int y){edge[++ ee].to = y; edge[ee].next = head[x]; head[x] = ee;}
void dfs1(int x, int fatt){
    dep[x] = dep[fatt] + 1; sz[x] = 1; fat[x] = fatt; son[x] = 0;
    for(int i = head[x]; i != -1; i = edge[i].next)if(edge[i].to != fatt){
        dfs1(edge[i].to, x), sz[x] += sz[edge[i].to];
        if(sz[edge[i].to] > sz[son[x]]) son[x] = edge[i].to;
    }
}
void dfs2(int x, int fatt){
    top[x] = fatt; ti[x] = ++ cnt; 
    if(son[x])dfs2(son[x], top[x]);
    for(int i = head[x]; i != -1; i = edge[i].next)
        if(edge[i].to != fat[x] && edge[i].to != son[x])dfs2(edge[i].to, edge[i].to);
}
void build(int t, int l, int r){
    tree[t].l = l; tree[t].r = r; tree[t].sum = tree[t].tag = 0;
    if(l == r)return; int mid = l + r >> 1;
    build(t + t, l, mid); build(t + t + 1, mid + 1, r);
}
inline void pushdown(int t){if(!tree[t].tag)return;
    (tree[t + t].tag += tree[t].tag) %= mod; (tree[t + t].sum += (tree[t].tag * (long long)(tree[t + t].r - tree[t + t].l + 1)) % mod) %= mod;
    (tree[t + t + 1].tag += tree[t].tag) %= mod; (tree[t + t + 1].sum += (tree[t].tag * (long long)(tree[t + t + 1].r - tree[t + t + 1].l + 1)) % mod) %= mod;
    tree[t].tag = 0;
}
inline void pushup(int t){tree[t].sum = (tree[t + t].sum + tree[t + t + 1].sum) % mod;}
void update(int t, int l, int r){
    if(tree[t].l >= l && r >= tree[t].r){
    (tree[t].sum += tree[t].r - tree[t].l + 1) %= mod; tree[t].tag ++; return;}
    int mid = tree[t].l + tree[t].r >> 1;
    pushdown(t);
    if(l <= mid)update(t + t, l, r); 
    if(r >= mid + 1)update(t + t + 1, l, r);
    pushup(t);
}
void add(int x){
    int l = ti[top[x]], r = ti[x];
    update(1, l, r);    
    if(top[x] != 1)add(fat[top[x]]);
}
int askk(int t, int l, int r){
    if(tree[t].l >= l && r >= tree[t].r){return tree[t].sum;}
    int mid = tree[t].l + tree[t].r >> 1, ret = 0;
    pushdown(t);
    if(l <= mid) ret += askk(t + t, l, r);
    if(r >= mid + 1) ret += askk(t + t + 1, l, r);
    return ret% mod;
}
int ask(int x){
    int l = ti[top[x]], r = ti[x];if(l == 0 || r == 0)return 0;
   int ret = askk(1, l, r); 
    return (ret + (top[x] == 1 ? 0 : ask(fat[top[x]]))) % mod;
}
int main()
{
    scanf("%d%d", &n, &q);
    memset(head, -1, sizeof(head));
    for(int i = 2; i <= n; i ++){int x; scanf("%d", &x); addedge(x + 1, i);}
    dfs1(1, 1); dfs2(1, 1); 
    build(1, 1, n);
    for(int i = 1; i <= q; i ++){
        int l, r, z; scanf("%d%d%d", &l, &r, &z);l ++; r ++; z ++;
        la[++ lala].wei = l - 1; la[lala].id = i; la[lala].flag = 0; la[lala].z = z;
        la[++ lala].wei = r; la[lala].id = i; la[lala].flag = 1;    la[lala].z = z;
    }
    sort(la + 1, la + lala + 1);int ll = 1;
    for(int i = 1; i <= lala; i ++){
        while(ll <= n && ll <= la[i].wei){add(ll); ll ++;    }  
        int ha = ask(la[i].z);
        if(la[i].flag)ans[la[i].id] += ha;
        else ans[la[i].id] -= ha;
    }
    for(int i = 1; i <= q; i ++)printf("%d\n", (ans[i] + mod) % mod);
    return 0;
}

这么简单的东西我硬是调了一个上午,,

因为 取模的时候 %= 写成了 % , 还有 最后的时候 (x + mod) % mod 没有加,,,

%¥#%¥&%**%&*&%& 这都什么逗比错误啊!!!受不了我自己了。


【总结反思】

以后 写取模的时候不要最后 再写, 最后写我就会因为 刚拍完一题然后心情很激动然后写得时候 就会飘飘然, 控制不住自己, 然后错一些莫名奇妙的错误,,,T^T

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值