首页 > 其他分享 >CF1797E 线段树 + 倍增 题解

CF1797E 线段树 + 倍增 题解

时间:2023-04-20 18:45:00浏览次数:43  
标签:log int 题解 线段 lca mid CF1797E mathcal id

Preface

有趣的一道 ds,赛后不看题解做出来了。

Solution

首先有一个性质:\(\varphi(x)\) 经过 \(\mathcal{O}(\log x)\) 次迭代后变为 \(1\)。

证明:

若 \(x\) 为奇数,\(\varphi(x)=x\sum_{i=1}^{k}\frac{p_i-1}{p_i}\),\(p_i\) 为奇数,所以 \(p_i-1\) 为偶数,我们就能得到 \(\varphi(x)\) 也为偶数。

若 \(x\) 为偶数,则 \(1\) 到 \(x-1\) 的所有偶数都与 \(x\) 不互质,所以 \(\varphi(x)\) 最大为 \(\frac{x}{2}\)。

这样每次下降 \(\frac{x}{2}\),\(\mathcal{O}(\log x)\) 次后变为 \(1\)。

证毕。

这样我们就可以每次倍增找两个数 \(a,b\) 经过 \(\varphi(x)\) 的迭代操作后第一次 \(a=b\) 的迭代次数,相当于是建了一棵高为 \(\log V\) 的树,\(x\) 的父亲为 \(\varphi(x)\),然后在上面找 lca,复杂度为 \(\mathcal{O}(\log \log V)\)。

那么我们直接建一棵线段树,维护区间 lca、最大深度与深度之和。

对于修改操作,直接对于最大深度不为 \(1\) 的区间暴力修改,这一部分总共是均摊 \(\mathcal{O}(n\log n\log V)\) 的,再加上 pushup 里修改 lca 的 \(\mathcal{O}(\log\log V)\),复杂度为 \(\mathcal{O}(n\log n\log V\log \log V)\)。

对于询问操作,合并所有点的 lca 操作为 \(\mathcal{O}(\log n\log\log V)\),答案即为区间深度和减 \(r-l+1\) 乘区间 lca 的深度,复杂度总共为 \(\mathcal{O}(m\log n\log\log V)\)。

所以时间复杂度即为 \(\mathcal{O}(n\log n \log V \log \log V+m\log n \log\log V)\)。

其实这个时间复杂度可以进一步优化成 \(\mathcal{O}(n\log n \log V+m\log n \log\log V)\),但是原时间复杂度已经足够通过此题,所以就没写。

Code:

#include<bits/stdc++.h>
using namespace std;
//#define int long long
#define pai 3.141592653589793238462643383279502884197169399375105820
#define pb emplace_back
#define fi first
#define se second
#define ls id << 1
#define rs id << 1 | 1
#define pc putchar
#define fre freopen("x.in", "r", stdin), freopen("x.out", "w", stdout)
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
void upmin(int &x, int y) {x = x < y ? x : y;}
void upmax(int &x, int y) {x = x > y ? x : y;}
typedef unsigned long long ull;
typedef pair<int, int> pii;
/* --------------- fast io --------------- */ // begin
//省略fast io部分
/* --------------- fast io --------------- */ // end
const int N = 1e5 + 5, maxn = 5e6 + 5, mod = 1e9 + 7, inf = 1e9;
int n, m, va[N], dep[maxn], anc[maxn][6];
int cnt, phi[maxn], p[N << 2]; bool vis[maxn];
vector<int> e[maxn];  
set<pii> s;
void euler()
{
    phi[1] = 1;
    for (int i = 2; i < maxn; i++)
    {
        if (!vis[i]) p[++cnt] = i, phi[i] = i - 1;
        for (int j = 1; j <= cnt && 1ll * i * p[j] < maxn; j++)
        {
            vis[i * p[j]] = 1;
            if (!(i % p[j]))
            {
                phi[i * p[j]] = phi[i] * p[j];
                break;
            }
            phi[i * p[j]] = phi[i] * (p[j] - 1);
        }
    }
}
void dfs(int u, int fa)
{
	for (auto v : e[u])
	{
		if (v == fa) continue;
		dep[v] = dep[u] + 1, anc[v][0] = u;
		dfs(v, u);
	}
}
void built()
{
    for (int i = 1; i <= n; i++) 
    {
        int x = va[i];
        while (x != phi[x] && s.find(pii(x, phi[x])) == s.end()) 
        {
            e[x].pb(phi[x]), e[phi[x]].pb(x);
            s.insert(pii(x, phi[x])), x = phi[x];
        }
    }
	dep[1] = 1;
	dfs(1, 0);
	for (int j = 1; j <= 5; j++)
		for (int i = 1; i < maxn; i++)
			anc[i][j] = anc[anc[i][j - 1]][j - 1];
}
int LCA(int u, int v)
{
	if (dep[u] < dep[v]) swap(u, v);
	for (int i = 5; i >= 0; i--)
		if (dep[anc[u][i]] >= dep[v]) u = anc[u][i];
	if (u == v) return u;
	for (int i = 5; i >= 0; i--)
		if (anc[u][i] != anc[v][i]) u = anc[u][i], v = anc[v][i];
	return anc[u][0];
}
struct node {int lca, mx, sum;};
struct segt
{
    node t[N << 2];
    #define ls id << 1
    #define rs id << 1 | 1
    void pushup(int id)
    {
        t[id].lca = LCA(t[ls].lca, t[rs].lca);
        t[id].mx = max(t[ls].mx, t[rs].mx);
        t[id].sum = t[ls].sum + t[rs].sum;
    }
    void build(int id, int l, int r)
    {
        if (l == r)
        {
            t[id].lca = va[l];
            t[id].sum = t[id].mx = dep[va[l]];
            return ;
        }
        int mid = (l + r) >> 1;
        build(ls, l, mid), build(rs, mid + 1, r);
        pushup(id);
    }
    void update(int id, int l, int r, int a, int b)
    {
        if (t[id].mx == 1) return ;
        if (l == r) 
        {
            t[id].lca = phi[t[id].lca];
            t[id].sum = t[id].mx = dep[t[id].lca];
            return ;
        }
        int mid = (l + r) >> 1;
        if (a <= mid) update(ls, l, mid, a, b);
        if (b > mid) update(rs, mid + 1, r, a, b);
        pushup(id);
    }
    pii query(int id, int l, int r, int a, int b)
    {
        if (a <= l && b >= r) return pii(t[id].lca, t[id].sum);
        int mid = (l + r) >> 1;
        pii res = pii(0, 0);
        if (a <= mid) res = query(ls, l, mid, a, b);
        if (b > mid)
            if (res == pii(0, 0)) res = query(rs, mid + 1, r, a, b);
            else 
            {
                pii x = query(rs, mid + 1, r, a, b);
                res.fi = LCA(res.fi, x.fi);
                res.se = res.se + x.se;
            }
        return res;
    }
    #undef ls
    #undef rs
}t;
signed main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> va[i];
    euler(), built();
    t.build(1, 1, n);
    while (m--)
    {
        int op, l, r; cin >> op >> l >> r;
        if (op == 1) t.update(1, 1, n, l, r);
        else
        {
            pii x = t.query(1, 1, n, l, r);
            cout << x.se - (r - l + 1) * dep[x.fi] << endl; 
        }
    }
    return 0;
} 

标签:log,int,题解,线段,lca,mid,CF1797E,mathcal,id
From: https://www.cnblogs.com/kowenxrz/p/17337927.html

相关文章

  • LeetCode-Go:一个使用 Go 语言题解 LeetCode 的开源项目
    在中国的IT环境里,大多数场景下,学习算法的目的在于通过笔试算法题。但算法书林林总总,有时候乱花渐欲迷人眼。杜甫有诗云:读书破万卷,下笔如有神。不管选择哪本书,只要深入学习,分层次,逐层进阶,一定可以将算法攻克。笔者强烈推荐一个Github开源项目LeetCode-Go,你不仅可以把他当做......
  • 2022上半年系统集成项目案例分析真题解析(广东卷)
    2022上半年系统集成项目案例分析真题解析(广东卷)......
  • Vscode 卡顿、CPU 过高问题解决
    原则:非必要不要搞很多Vscode的插件,Vscode本身插件很强大,但是非必要不要使用很多插件。在VSCode扩展市场目前其实存在着不少下载量特别高但是不应该再被使用的扩展,显然官方是不可能直接给你标出来哪些扩展已经被废弃了,哪些有严重bug,纯靠扩展作者自觉。第一步:Ctrl+Shift+P:D......
  • (IDEA)spring项目导入本地jar包方法和项目打包时找不到引入本地jar包的问题解决方案
    转:(IDEA)spring项目导入本地jar包方法和项目打包时找不到引入本地jar包的问题解决方案 【Maven】理解maven的6大内置属性   ......
  • 力扣题解分享1043.分割数组以得到最大和
    1043.分隔数组以得到最大和题目描述给定一个整数数组arr和一个整数k,将该数组分隔为长度最多为k的一些连续子数组。分隔完成后,每个子数组中的所有值都会变为该子数组中的最大值。返回将数组分隔变换后能够得到的元素最大和。示例input:arr=[1,15,7,9,2,5,10]k=......
  • GYM104081 部分题解
    比赛链接:https://codeforces.com/gym/104081目前就做了8题,里面还有4个水题……水题:ACEG,模拟题意即可,C和E有一些细节。不想写题解了F首先目标是如何将这9个数分组,由于答案一定存在,考虑随机化,固定\(a_1\inS_1\),然后随机一个\(a_i\inS_1\),异或得到\(S_1\)的另一......
  • ARC159F Good Division【性质,DP,线段树】
    定义一个序列是好的当且仅当其可以通过每次删去一对相邻的不同的数把序列删空。给定一个长度为\(2n\)的序列\(a\),求有多少种划分方式使得每一段都是好的。答案对\(998244353\)取模。\(n\leq5\times10^5\),时限\(\text{5.0s}\)。先考虑什么样的数列是合法的,显然必要条......
  • Codeforces Round 850 (Div. 2, based on VK Cup 2022 - Final Round) E. Monsters (h
    传送门详细题解传送门  抄的ygg代码,向在这里说一下刚开始没看懂的部分。  求答案的时候是把所有的当前为止的所有数值加起来减去一个从1开始并且公差为1的等差数列的前size项和。其中size是当前最多能用到哪个位置,满足前size项能构成1,2,3,....,sz这样的形式。  假设我们......
  • npm install karma时报错的问题解决
    karma在js自动化测试方面很有名,但是安装的时候出的问题npminstall-gkarma 报错好像是socket.iosocket.io.client依赖时报出的错误 看到网上回复说先装下这个:有人说要先装下这个:npminstall-gnode-gyp 试了下问题没有解决。 又有回复说要装这个:npminstall-gws 装好之......
  • NOIP 2010 题解
    机器翻译单向链表,如果\(i\)在内存里,那么用\(nxt[i]\)来记录他的下一个单词,每次要插入的时候,如果当前链表的长度小于\(m\),那么直接把他插入的末尾,如果等于\(m\),就把链表的第一个从链表里弹出来,再把这个元素加进去。\(Code:\)#include<bits/stdc++.h>usingnamespacest......