首页 > 其他分享 >【题解】CF1178G The Awesomest Vertex

【题解】CF1178G The Awesomest Vertex

时间:2023-01-06 19:25:01浏览次数:51  
标签:const int 题解 top Vertex pos CF1178G maxn line

题意

给定一棵大小为 \(n\) 的树以及 \(m\) 个操作,

定义一个结点 \(u\) 的权值为:

\(|\sum\limits_{w \in R(v)} a_w| \cdot |\sum\limits_{w \in R(v)} b_w|\)

其中 \(R(v)\) 表示结点 \(v\) 的祖先结点(含自身)

每次操作可以:

  1. 将结点 \(u\) 的权值加 \(x\)

  2. 询问结点 \(u\) 的子树内最大权值

\(n, m \leq 2 \times 10^5\)

思路

分块 + 凸包。

首先子树问题考虑 dfs 序拍平成序列问题,发现 \(|\sum\limits_{w \in R(v)} b_w|\) 可以直接预处理出来,是一个常数。

那么每次操作的影响形如 \(|\sum\limits_{w \in R(v)} a_w + x| \cdot b(v)\),将 \(b(v)\) 看成斜率,那么这里就是一条直线的形式。

绝对值很烦,考虑变成 \(\max(a_w + x, -a_w - x)\) 分别维护,最后取较大值即可。

于是问题变成在序列上维护若干条直线,每次问在特定位置的最高点坐标,是凸包套路。

但是,在序列上维护区间凸包?显然线段树不可做,考虑万用分块。

每次修改,对于整块中的直线整体上移,所以只需要考虑给它打求和标记。对于散块可以暴力重构。

每次询问,对于整块可以直接在凸包上二分求最高点坐标,对于散块可以暴力查询。

发现这样做的复杂度带一只 \(\log\),考虑优化。

首先 \(x\) 坐标单调,可以直接指针代替二分。

每次重构会将直线按斜率升序加入单调栈,这里可以初始时按斜率排好序。

时间复杂度 \(O(n \sqrt{n})\),magic!

代码

#include <cstdio>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std;

typedef long long ll;

const int maxn = 4e5 + 5;

int n, m;
int cnt, bs = 10;
int a[maxn], b[maxn], inp[maxn], outp[maxn];
vector<int> g[maxn];

struct line
{
    int k;
    ll b;
    inline ll gety(int x) { return 1ll * k * x + b; }
    inline void update(int x) { b += 1ll * k * x; }
} ;

double interx(const line& a, const line& b)
{
    if (a.k == b.k) return (a.b > b.b ? 1e10 : -1e10);
    return (double)(b.b - a.b) / (a.k - b.k);
}

struct block
{
    line q[305], *a;
    int x, pos, top;
    double k[305];

    ll query()
    {
        while ((pos < top) && (k[pos + 1] < x)) pos++;
        return q[pos].gety(x);
    }

    void build()
    {
        if (x) for (int i = 0; i < bs; i++) a[i].update(x);
        x = top = 0, pos = 1;
        for (int i = 0; i < bs; i++)
        {
            while ((top > 1) && (k[top] > interx(q[top], a[i]))) top--;
            q[++top] = a[i];
            k[top] = interx(q[top - 1], q[top]);
        }
    }
} ;

struct data
{
    line x;
    int p;

    bool operator < (const data& rhs) const { return (x.k == rhs.x.k ? x.b < rhs.x.b : x.k < rhs.x.k); }
} seq[305];


struct item
{
    block b[705];
    line a[maxn];
    int st[maxn];
    
    void build()
    {
        for (int i = 0; i <= n; i += bs)
        {
            b[i / bs].a = a + i;
            for (int j = 0; j < bs; j++) seq[j] = (data){a[j + i], j + i};
            sort(seq, seq + bs);
            for (int j = 0; j < bs; j++)
            {
                a[j + i] = seq[j].x;
                st[seq[j].p] = j + i;
            }
            b[i / bs].build();
        }
    }

    ll query(int l, int r)
    {
        int bl = l / bs, br = r / bs;
        ll ans = -(1ll << 60);
        if (bl == br) for (int i = l; i <= r; i++) ans = max(ans, a[st[i]].gety(b[bl].x));
        else
        {
            bl++;
            for (int i = l; i < bl * bs; i++) ans = max(ans, a[st[i]].gety(b[bl - 1].x));
            for (int i = br * bs; i <= r; i++) ans = max(ans, a[st[i]].gety(b[br].x));
            for (int i = bl; i < br; i++) ans = max(ans, b[i].query());
        }
        return ans;
    }

    void update(int l, int r, int x)
    {
        int bl = l / bs, br = r / bs;
        if (bl == br)
        {
            for (int i = l; i <= r; i++) a[st[i]].update(x);
            b[bl].build();
        }
        else
        {
            bl++;
            for (int i = l; i < bl * bs; i++) a[st[i]].update(x);
            b[bl - 1].build();
            for (int i = br * bs; i <= r; i++) a[st[i]].update(x);
            b[br].build();
            for (int i = bl; i < br; i++) b[i].x += x;
        }
    }
} s1, s2;

inline int read()
{
    int res = 0, flag = 1;
    char ch = getchar();
    while ((ch < '0') || (ch > '9'))
    {
        if (ch == '-') flag = -1;
        ch = getchar();
    }
    while ((ch >= '0') && (ch <= '9'))
    {
        res = res * 10 + ch - '0';
        ch = getchar();
    }
    return res * flag;
}

inline void write(ll x)
{
    if (x < 0) putchar('-'), x = -x;
    if (x > 9) write(x / 10);
    putchar(x % 10 + '0');
}

void dfs(int u)
{
    inp[u] = ++cnt;
    for (int v : g[u])
    {
        a[v] += a[u], b[v] += b[u];
        dfs(v);
    }
    outp[u] = cnt;
}

int main()
{
    scanf("%d%d", &n, &m);
    while (bs * bs * 12 <= n * 5) bs++;
    for (int i = 2, f; i <= n; i++)
    {
        scanf("%d", &f);
        g[f].push_back(i);
    }
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for (int i = 1; i <= n; i++) scanf("%d", &b[i]);
    dfs(1);
    s1.a[0] = s2.a[0] = (line){0, -(1ll << 60)};
    for (int i = 1; i <= n; i++)
    {
        if (b[i] < 0) b[i] = -b[i];
        s1.a[inp[i]] = (line){b[i], 1ll * a[i] * b[i]};
        s2.a[inp[i]] = (line){-b[i], -1ll * a[i] * b[i]};
    }
    for (int i = n + 1; i < n / bs * bs + bs; i++) s1.a[i] = s2.a[i] = (line){0, -(1ll << 60)};
    s1.build(), s2.build();
    for (int i = 1, opt, u, x; i <= m; i++)
    {
        scanf("%d", &opt);
        if (opt == 1)
        {
            scanf("%d%d", &u, &x);
            s1.update(inp[u], outp[u], x);
            s2.update(inp[u], outp[u], x);
        }
        else
        {
            scanf("%d", &u);
            printf("%lld\n", max(s1.query(inp[u], outp[u]), s2.query(inp[u], outp[u])));
        }
    }
    return 0;
}

标签:const,int,题解,top,Vertex,pos,CF1178G,maxn,line
From: https://www.cnblogs.com/lingspace/p/cf1178g.html

相关文章

  • 230102模拟题解
    t1容易发现对于奇数和偶数,能满足条件的长度分别是单调的,所以可以分别二分答案检查。检查的时候对于差分序列做哈希即可,然后用set/map/哈希表判\(B\)的子段是否有\(A......
  • CF865B Ordering Pizza 题解
    简要题意:有\(n\)个人去披萨店吃披萨,有两种披萨,每个披萨有\(m\)片。现在第\(i\)个人要吃\(c_i\)片披萨,如果吃一片第一种披萨会获得\(a_i\)的幸运值,如果吃一片第二......
  • configmap出现/n问题解决
    1.现象原始文件肉眼显示正常,如下图命令行显示整个data部分成了一坨,回车全变成了/n,虽然不影响使用,但是对维护查看比较麻烦,如下图2.问题原因1.配置文件里有一些Tab......
  • [CTSC2009]魔幻花园 题解
    [CTSC2009]魔幻花园题解洛谷链接。一、问题转化原问题等价于求某个水平面上所有点围成凸包的面积的最小值。首先考虑把三维问题转化到二维上:考虑到任意时刻所有点都在......
  • I. 西行妖下题解
    题目内容原题链接样例输入5201样例输出?44232?35155?52431!32154题解首先,题目有一个很难解决的痛点,就是他只会返回最前面的那......
  • Codeforces Round #842 (Div. 2) A-D题解
    比赛链接A、GreatestConvex非常的简单。根据样例直接猜是输出\(n-1\).上一个\(Python\)代码。T=int(input())whileT>0:T-=1n=int(input())......
  • USACO Au题解
    T1一眼背包,但是很怪考虑设dp[i][j][k]表示前i个物品还剩j个月饼还剩k个冰激凌。$O(n^4)$显然。转移O(n)枚举用钱还是优惠券。瓶颈在于冰激凌的优惠。考虑如何把这一......
  • idea 内存参数修改不生效问题解决 VM参数设置不生效解决
    提示:在idea的工具栏Help->EditCustomVMOptions内修改 对应参数-Xmx1024m后重启无效的再看下面的方法 问题:ieda的默认内存大小是1024M当我开多个工......
  • PIP 更新后不能使用的使用 提示: No module named 'pip'问题解决
    1、问题引入   正确安装Python以后,Python和PIP都可以正常使用。在使用pip安装其他库的时候,提示PIP版本过低,建议更新,结果更新时发生错误,导致PIP不能被识别,具体如下图......
  • 【题解】P2305 [NOI2014] 购票
    题意给定一棵边带权且以\(1\)为根的树,从后代结点\(u\)跳到祖先结点\(v\)的代价为\(dp_u+q_u\),其中\(p_u,q_u\)是给定的常数,\(d\)是\(u,v\)的树上距离。要......