首页 > 其他分享 >闲话 22.11.3

闲话 22.11.3

时间:2022-11-03 21:36:00浏览次数:80  
标签:10 ##_ ch get int 闲话 22.11 le

闲话

好今日得到最让人感到奇妙的一件事是松鼠加入 ps_p live 力
虽然但是……比较地奇妙
……不知道该评价什么(
连步玎都不是 ps_p 的(

所以给了一天自由练习的时间就是刷数据结构的是吗(

然后看了一半的 Lycoris Recoil
强推!下次要看完(
今日推的原创番就是莉可丽丝了!

就先写到这罢
白鸟过河滩好听的!

数据结构……杂题?

这是五道题。明天似乎还有五道。
也是比较的水,所以题解写得短点(

CF1422F

给定长为 \(n\) 的序列 \(a\)。\(q\) 次询问 \((l,r)\),你需要输出

\[\mathop{\operatorname{lcm}}\limits_{i=l}^r \{a_i\} \]

对 \(10^9 + 7\) 取模的值。强制在线。

\(1\le n, q\le 10^5, 1\le a_i \le 2\times 10^5\)。

Muel的常数怎么那么小啊啊啊啊

考虑将每个数质因子分解。对每个 \(n\) 有 \(O(\log n)\) 个状态。注意这里的状态不一定是质因子 \(p\) 和其最大指数的组合,非最大指数也可以。我们发现,我们需要的是 \([l,r]\) 区间内元素对应 \(p^k\) 状态中满足 \(k\) 最大的状态的积。
考虑对每个元素维护一个前缀版本,当一个元素可以贡献 \(p^k\) 时将其前面所有可以贡献 \(p^k\) 的元素的贡献删除。容易发现可以让每个元素只删除直接前驱做到这一点。
对每个贡献开桶,维护存在该贡献的直接前驱。当出现新的元素可以产生贡献时,在该元素版本中删除前驱并加入当前节点。这点可以使用主席树,根维开在下标上维护前缀版本,值维同样开在下标上维护某个前缀版本中特定位置的信息。

时空复杂度 \(O(n\log^2 n)\)。

通过对质因子进行根号分治也可以做到。空间复杂度有些问题。

code
#include <bits/stdc++.h>
using namespace std;
template<typename T> void get(T & x) {
	x = 0; char ch = getchar(); bool f = false; while (ch < '0' or ch > '9') f = f or ch == '-', ch = getchar();
	while ('0' <= ch and ch <= '9') x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); f && (x = -x); 
} template <typename T, typename ... Args> void get(T & a, Args & ... b) { get(a); get(b...); }
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
const int N = 2e5 + 10, mod = 1e9 + 7;
int n, q, a[N], mx, l, r, ret, inv[N], lpf[N], pref[N];

struct SegmentCitrus {
    int rt[N], mlc;
    int & operator [] (const int & p) { return rt[p]; }
    const int operator [] (const int & p) const { return rt[p]; }

    struct node {
        int ls, rs, sum;
        #define ls(p) seg[p].ls
        #define rs(p) seg[p].rs
        #define sum(p) seg[p].sum
    } seg[N * 200];

    int add(int p, int l, int r, int pos, int val) {
        int ptr = ++mlc;
        if (l == r) {
            sum(ptr) = 1ll * sum(p) * val % mod;
            return ptr;
        } int mid = l + r >> 1;
        if (pos <= mid) ls(ptr) = add(ls(p), l, mid, pos, val), rs(ptr) = rs(p);
        else ls(ptr) = ls(p), rs(ptr) = add(rs(p), mid + 1, r, pos, val);
        sum(ptr) = 1ll * sum(ls(ptr)) * sum(rs(ptr)) % mod;
        return ptr;
    }
    void add(int id, int pos, int val) {
        rt[id] = add(rt[id], 1, n, pos, val);
    }

    int qry(int p, int l, int r, int bnd) {
        if (l >= bnd) return sum(p);
        if (r < bnd) return 1;
        int mid = l + r >> 1;
        return 1ll * qry(ls(p), l, mid, bnd) * qry(rs(p), mid + 1, r, bnd) % mod;
    }
    int qry(int l, int r) { return qry(rt[r], 1, n, l); }
} Tr;

signed main() {
    get(n); rep(i,1,n) get(a[i]), mx = max(mx, a[i]);

    inv[0] = inv[1] = 1;
    rep(i,2,mx) {
        inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;
        if (!lpf[i]) for (int j = i; j <= mx; j += i) lpf[j] = i;
    }
    Tr.seg[0].sum = 1;
    rep(i,1,n) {
        int tmp = a[i]; Tr[i] = Tr[i-1];
        while (lpf[tmp]) {
            int k = lpf[tmp], t = 1;
            while (tmp % k == 0) {
                t *= k; tmp /= k;
                if (pref[t]) Tr.add(i, pref[t], inv[k]);
                pref[t] = i;
            } 
            Tr.add(i, i, t);
        }
    } 

    get(q); rep(i,1,q) {
        get(l, r); 
        l = (l + ret) % n + 1, r = (r + ret) % n + 1;
        if (l > r) swap(l, r);
        cout << (ret = Tr.qry(l, r)) << '\n';
    }
}


CF1706E

给出 \(n\) 个点, \(m\) 条边的不带权连通无向图, \(q\) 次询问至少要加完编号前多少的边,才能使得 \([l,r]\) 中的所有点两两连通。

\(1\le n, m, q \le 2\times 10^5\)。

考虑一条边的边权为编号。记 \(f(u,v)\) 为 \(u,v\) 在最小生成树上路径中边权最大值。容易发现答案即为

\[\max_{i=l}^{r-1} \{ f(i,i+1) \} \]

如果我们可以求得 \(f\),那就可以通过 st 表等结构快速得到答案。问题来到了如何求得 \(f\)。
这是很容易的。使用 Kruskal 重构树可以做到 \(O(n\log n)\) 求解。

总时间复杂度 \(O(n\log n)\)。

code
#include <bits/stdc++.h>
using namespace std;
template<typename T> void get(T & x) {
	x = 0; char ch = getchar(); bool f = false; while (ch < '0' or ch > '9') f = f or ch == '-', ch = getchar();
	while ('0' <= ch and ch <= '9') x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); f && (x = -x); 
} template <typename T, typename ... Args> void get(T & a, Args & ... b) { get(a); get(b...); }
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
const int N = 1e5 + 10;
int T, n, m, q, l, r;

int dsu[N], ele[N];
void init(int n) { rep(i,1,n) dsu[i] = ele[i] = i; } 
int find(int u) { return u == dsu[u] ? u : dsu[u] = find(dsu[u]); }

int dfn[N << 1], stp, dep[N << 1], a[N << 1], fa[N << 1], cnt, f[N], lgv[N << 1], st[20][N << 1];
vector <int> g[N << 1];
void dfs(int u, int f) {
    dfn[u] = ++ stp, fa[u] = f; dep[u] = dep[f] + 1;
    for (auto v : g[u]) if (v != f) dfs(v, u);
}

void init() {
    rep(i,2,stp) lgv[i] = lgv[i >> 1] + 1;
    rep(i,1,stp) st[0][dfn[i]] = fa[i];
    rep(i,1,lgv[stp]) for (int j = 1, u, v; j + (1 << i) - 1 <= stp; ++ j) 
        u = st[i-1][j], v = st[i-1][j + (1 << i - 1)], st[i][j] = dep[u] < dep[v] ? u : v;
}

int qry(int u, int v) {
    if (u == v) return u;
    u = dfn[u], v = dfn[v];
    if (u > v) swap(u, v); u ++;
    int d = lgv[v - u + 1];
    u = st[d][u], v = st[d][v - (1 << d) + 1];
    return dep[u] < dep[v] ? u : v;
}

void init2() {
    rep(i,1,n-1) st[0][i] = f[i];
    rep(i,1,lgv[n-1]) for (int j = 1; j + (1 << i) <= n; ++ j) 
        st[i][j] = max(st[i-1][j], st[i-1][j + (1 << i - 1)]);
}

int query(int l, int r) {
    if (l > r) return 0;
    int d = lgv[r - l + 1];
    return max(st[d][l], st[d][r - (1 << d) + 1]);
}

signed main() {
    get(T);
    while (T--) {
        get(n, m, q); cnt = n;
        rep(i,1,(n<<1) - 1) g[i].clear();
        init(n);
        rep(i,1,m) {
            get(l, r);
            l = find(l), r = find(r);
            if (l == r) continue;
            ++ cnt;
            g[cnt].push_back(ele[l]), g[cnt].push_back(ele[r]);
            g[ele[l]].push_back(cnt), g[ele[r]].push_back(cnt);
            dsu[l] = r, a[cnt] = i, ele[r] = cnt;
        } 
        dfs(cnt, 0); init();
        rep(i,1,n-1) f[i] = a[qry(i, i + 1)];
        init2();
        rep(i,1,q) {
            get(l, r);
            cout << query(l, r - 1) << ' ';
        } cout << '\n';
    }
}


CF1446C

给定序列 \(a\),保证 \(a_i\) 两两不同。每个 \(i\)会向序列中满足 \(a_i\oplus a_j\)(\(\oplus\) 代表异或)最小的 \(j\) 连双向边,并且如果 \(j\) 也向 \(i\) 连了边,只算一条边。现在要删去序列中的一些数,使得最后形成的图是一棵树,输出最少删除数。

\(1\le n\le 2\times 10^5, 1\le a_i \le 10^9\)。

转化成最大保留数计算,答案转为 $n - $ 答案即可。

考虑异或最小才连边的性质,我们将所有数插入一棵 trie。容易发现只有最深层节点才会和他的兄弟连边,这就组成了一系列长度不超过 \(2\) 的环。
设 \(f_i\) 为 \(i\) 节点子树内元素两两有边的最大保留数。容易发现当 \(i\) 的孩子数小于 \(2\) 时直接继承儿子即可。考虑两个儿子的情况。由于一边剩余超过两个节点的话一定会在内部连边,因此定有一边需要删除节点到只剩一个。取两边较小的删除即可。

在 trie 上进行一遍搜索即可得到答案。复杂度 \(O(n\log a)\)。

code
#include <bits/stdc++.h>
using namespace std;
template<typename T> void get(T & x) {
	x = 0; char ch = getchar(); bool f = false; while (ch < '0' or ch > '9') f = f or ch == '-', ch = getchar();
	while ('0' <= ch and ch <= '9') x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); f && (x = -x); 
} template <typename T, typename ... Args> void get(T & a, Args & ... b) { get(a); get(b...); }
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
const int N = 2e5 + 10;
int n, a[N], mx, lgmx;
 
int trie[N << 5][2], mlc;
void insert(int u) {
    int ptr = 0, ztr;
    pre(i,lgmx,0) {
        ztr = u >> i & 1;
        if (trie[ptr][ztr] == 0) trie[ptr][ztr] = ++ mlc;
        ptr = trie[ptr][ztr];
    }
}
 
int dfs(int u) {
    if (!trie[u][0] and !trie[u][1]) return 1;
    if (!trie[u][0] and trie[u][1]) return dfs(trie[u][1]);
    if (trie[u][0] and !trie[u][1]) return dfs(trie[u][0]);
    return max(dfs(trie[u][0]), dfs(trie[u][1])) + 1;
}
 
signed main() {
    get(n); rep(i,1,n) get(a[i]), mx = max(mx, a[i]); lgmx = __lg(mx);
    rep(i,1,n) insert(a[i]);
    cout << n - dfs(0);
}


CF455D

给定长度为 \(n\) 的序列 \(a\),共有 \(q\) 个询问,支持两种操作:

  • 1 l r 将区间 \([l,r]\) 依次向右移动一位,其中 \(a_r\) 移动到 \(a_l\)。

  • 2 l r k 询问区间 \([l,r]\) 中 \(k\) 出现次数。

强制在线。

\(1\le n,q\le 10^5,1\le a_i\le n\)。

这题我没打正解。

首先考虑静态情况怎么写。
这是简单的,我们开 \(n\) 个 vector,第 \(i\) 个 vector 维护值为 \(i\) 的元素的编号集合,每次查询时找到落在前缀区间内的元素个数后差分得到答案。

由于每次只会移动一个元素,考虑动态为元素重编号。
具体地,我们维护一个平衡树表示当前所有元素的编号。假设要插入的位置前面的元素的编号为 \(l\),位置后面的元素的编号为 \(r\),则为更改元素重编号为 \(\frac{l+r}2\)。
询问时找到第 \(l,r\) 个编号,仍然在对应值的集合中寻找前缀后差分即可。

这引发了一个问题:若数据是特殊构造的,则连续向一个位置插入会导致两个点的编号以指数速度趋近,最后精度丢失成为相同点。
这显然无法再使用平衡树维护了。
怎么办呢?
答案是遇到困难睡大觉。
当产生这种情况时我们直接退出程序就能过这题的 hack 数据了。因为这题的 hack 完全没有一次询问。

当然这也是可以解决的。可能的方案有采取不同的重编号策略多次重复尝试或 \(O(\frac{\sqrt{n}} {\log {n}})\) 次插入后暴力重构等。
不想写。
正解是平衡树维护这个相对顺序,或者分块。
不想写。

因为不想写 \(n+1\) 棵平衡树所以仍然是 vector 维护编号集合。
因为不想写 \(1\) 棵平衡树所以仍然是 pb_ds 的平衡树。
所以码很短。

code
#include <bits/stdc++.h>
#include <bits/extc++.h>
using namespace std;
template<typename T> void get(T & x) {
	x = 0; char ch = getchar(); bool f = false; while (ch < '0' or ch > '9') f = f or ch == '-', ch = getchar();
	while ('0' <= ch and ch <= '9') x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); f && (x = -x); 
} template <typename T, typename ... Args> void get(T & a, Args & ... b) { get(a); get(b...); }
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
const int N = 2e5 + 10;
int n, q, typ, l, r, x, ans;
vector <double> bv[N];
__gnu_pbds :: tree <pair<double, int>, __gnu_pbds :: null_type, less<pair<double,int> >, 
            __gnu_pbds :: rb_tree_tag, __gnu_pbds :: tree_order_statistics_node_update> T;

signed main() {
    get(n); rep(i,1,n) get(x), bv[x].push_back(i * 1e18), T.insert( { i * 1e18, x } );
    get(q); while ( q-- ) {
        get(typ, l, r); l = (l + ans - 1) % n + 1; r = (r + ans - 1) % n + 1; if (l > r) swap(l, r);
        if (typ == 1) {
            auto lft = prev(T.find_by_order(l - 1)), rgt = T.find_by_order(l - 1), rpos = T.find_by_order(r - 1);
            bv[rpos->second].erase(lower_bound(bv[rpos->second].begin(), bv[rpos->second].end(), rpos->first));
            bv[rpos->second].insert(upper_bound(bv[rpos->second].begin(), bv[rpos->second].end(), (lft->first + rgt->first) / 2), (lft->first + rgt->first) / 2);
            int tmp = rpos->second;
            T.erase(rpos);
            if ((T.find( {(lft->first + rgt->first) / 2, tmp} ) != T.end())) return 0;
            T.insert( {(lft->first + rgt->first) / 2, tmp} );
        } else {
            auto lft = T.find_by_order(l - 1), rgt = T.find_by_order(r - 1);
            get(x); x = (x + ans - 1) % n + 1;
            cout << ( ans = (upper_bound(bv[x].begin(), bv[x].end(), rgt->first) - bv[x].begin()) -
                            (lower_bound(bv[x].begin(), bv[x].end(), lft->first) - bv[x].begin()) ) << '\n';
        }
    }
}


CF1635F

给定 \(n\) 个二元组 \((x_i,w_i)\),按照 \(x_i\) 严格递增排序给出。给出 \(q\) 次询问,每次询问给出 \(l,r\),你需要输出:

\[\min_{l\le i<j\le r} |x_i-x_j| \times (w_i+w_j) \]

\(2\le n,q \le 3\times 10^5, |x_i|\le 10^9, 1\le w_i \le 10^9\)。

性质题。

我们从左右两边对 \(w\) 做单调栈,栈内维护小于 \(w_i\) 的值中 \(x\) 最接近 \(x_i\) 的元素。设 \(l_i\) 为从左边做到 \(i\) 位置时 \(i\) 入栈前的栈顶,\(r_i\) 为从右边做时的栈顶。我们需要证明只有 \((i,l_i)\) 和 \((i,r_i)\) 可能对答案造成贡献。
证明:
假设 \((i,j)\) 是更优的答案且 \(j\neq l_i, j\neq r_i\),则我们知道 \(w_{l_i} \le w_j, |x_{l_i} - x_i| < |x_j - x_i|\)。因此 \(j\) 的答案一定不更优。
\(r_i\) 的情况同理。

于是我们就只需要维护 \(O(n)\) 对元素对答案的贡献了。容易发现每对元素是一个带权区间,需要询问区间完全包含才能贡献权值,最终答案是权值最小值。
这个直接树状数组倒着扫一遍就行了。在区间结束时以区间开始位置作为下标插入区间权值即可。

答案最大 \(4\times 10^{18}\),记得 \(\inf\) 设大点。

code
#include <bits/stdc++.h>
using namespace std;
template<typename T> void get(T & x) {
	x = 0; char ch = getchar(); bool f = false; while (ch < '0' or ch > '9') f = f or ch == '-', ch = getchar();
	while ('0' <= ch and ch <= '9') x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); f && (x = -x); 
} template <typename T, typename ... Args> void get(T & a, Args & ... b) { get(a); get(b...); }
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
const int N = 3e5 + 10; typedef long long ll;
int n, q, x[N], w[N], t1, t2;
vector <tuple<int, int, ll> > vec[N];
int stk[N], top;
ll ans[N];

#define calc(i, j) (1ll * abs(x[i] - x[j]) * (w[i] + w[j]))

struct BIT {
    ll Index[N]; 
    void add(int p, ll v) { for (; p <= n; p += p & -p) Index[p] = min(Index[p], v); }
    ll qry(int p) { ll ret = 4e18 + 10; for (; p; p ^= p & -p) ret = min(ret, Index[p]); return ret; }
} B;

signed main() {
    get(n, q); rep(i,1,n) get(x[i], w[i]), B.Index[i] = 4e18 + 10;
    stk[++top] = 0;
    rep(i,1,n) {
        while (stk[top] and w[stk[top]] > w[i]) -- top;
        if (stk[top]) vec[stk[top]].emplace_back(0, i, calc(stk[top], i));
        stk[++top] = i;
    }
    stk[top = 1] = 0;
    pre(i,n,1) {
        while (stk[top] and w[stk[top]] > w[i]) -- top;
        if (stk[top]) vec[i].emplace_back(0, stk[top], calc(stk[top], i));
        stk[++top] = i;
    }
    rep(i,1,q) get(t1, t2), vec[t1].emplace_back(i, t2, 0);
    
    pre(i,n,1) {
        for (auto [id, pos, val] : vec[i]) 
            if (id == 0) {
                B.add(pos, val);
            } else {
                ans[id] = B.qry(pos);
            }
    } rep(i,1,q) cout << ans[i] << '\n';
}

标签:10,##_,ch,get,int,闲话,22.11,le
From: https://www.cnblogs.com/joke3579/p/chitchat221103.html

相关文章

  • 2022.11.03 NOIP2022 模拟赛二
    绯色IOI(开端)之前做过了,见杂题题解(一),话说这个系列是不是好久没更新了。CodeconstintN=2e5+5;intn,m,a[N];intmain(){n=read();FOR(i,1,n)a[i]=read(......
  • [2022.11.02] 模拟赛 第四题
    \(V\)\(Problem\)给定一棵\(n\)个点的树,初始每条边长度都为\(1\),每次操作你需要选择一条边并令其长度增加\(1\)。给定\(Q\)次询问,每次给定一个数\(K_i\),你必须恰......
  • 【2022.11.03】pytorch的使用相关
    Pytorch的使用相关,学习来源:https://www.bilibili.com/video/BV1Wv411h7kN/?p=6加载数据有两种方法,一种是torch.utils.data.Dataset,一种是torch.utils.data.DataloaderTe......
  • 2022.11.2每日一题
    DaimayuanOnlineJudge-整齐的数组题目描述\(Polycarp\)有一个长度为\(n\)的数组\(a_1,a_2,...,a_n\)(\(n\)是偶数)。\(Polycarp\)还得到了一个正整数\(k\),他开......
  • 2022.11.1每日一题
    DaimayuanOnlineJudge-网格判断题目描述您将获得一个\(n×n\)的网格,网格中每个正方形的颜色为黑色或白色。如果满足以下所有条件,则网格是正确的:每行的黑色方块数......
  • 2022.11.2 模拟赛题解
    简要题意给定一棵\(n\)个节点的有根树,树根为\(1\)号节点,每个结点有一个权值\(a_i(|a_i|\leq10^9)\),求包含\(1\)的前\(k\)小的连通块的权值。简要题解前置内......
  • 【2022.11.2】Vue基础学习(7)
    内容详细1vue3介绍1.性能的提升打包大小减少41%初次渲染快55%,更新渲染快133%内存减少54%2.源码的升级使用Proxy代替defineProperty实现响应式......
  • [2022.11.2]collection
    collection接口1.单列集合框架结构l----collection接口:单列集合,用来存储一个一个的对象/----List接口:存储序的、可重复的数据。-->“动态”数组/--......
  • 【2022.11.02】机器学习名词记录
    学习来源均来自:https://www.bilibili.com/video/BV1Wv411h7kN当我们设定函数的时候,不一定是线性的情况下,可以将一个曲线图拆分成多个函数相加正如下图中的红色图,是由四......
  • 【闲话】2022.11.01
    今天是冬月的第一天万圣节dsu晚上会去大家屋里要糖的说起来很久没喝南瓜粥了今日一推这种东西,本来就是越离谱越好阴蜂(早就)已经有理论解了大家要不去打一下((说起来......