Java教程

2022.8.3 颓废记录

本文主要是介绍2022.8.3 颓废记录,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Preface

数据结构题太珂怕了QAQ

Content

[CF165D]Beard Graph

给定一棵 \(n\) 个结点的数,初始所有边均为黑边,\(m\) 次操作,操作分 \(3\) 种:

  • 1 u:把第 \(u\) 条边变成黑边。

  • 2 u:把第 \(u\) 条边变成白边

  • 3 u v:若 \((u,v)\) 路径上存在白边则输出 -1,否则输出路径上的黑边数量。

\(1 \le n \le 10^5,1\le m \le 3\times 10^5\)

树剖模板,就当复习树剖了qwq。

时间复杂度 \(O(M\log^2N)\)

Code

// Problem: CF165D Beard Graph
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF165D
// Memory Limit: 250 MB
// Time Limit: 4000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
using namespace std;
const int maxn = 1e5 + 5;
int f[maxn],d[maxn],son[maxn],sz[maxn],top[maxn],rk[maxn],id[maxn],cnt;
int ls[maxn << 2],rs[maxn << 2],sum[maxn << 2];
void pushup(int i) {
    sum[i] = sum[i << 1] + sum[i << 1 | 1];
    return ;
}
void build(int i,int l,int r) {
    ls[i] = l;
    rs[i] = r;
    if(l == r) {
        sum[i] = 1;
        return ;
    }
    int mid = l + r >> 1;
    build(i << 1 , l , mid);
    build(i << 1 | 1 , mid + 1 , r);
    pushup(i);
    return ;
}
void modify(int i,int pos,int val) {
    if(ls[i] == rs[i]) {
        sum[i] = val;
        return ;
    }
    int mid = ls[i] + rs[i] >> 1;
    if(pos <= mid)modify(i << 1 , pos , val);
    else modify(i << 1 | 1 , pos , val);
    pushup(i);
    return ;
}
int query(int i,int l,int r) {
    if(ls[i] >= l&&rs[i] <= r)return sum[i];
    if(ls[i] > r||rs[i] < l)return 0;
    int mid = ls[i] + rs[i] >> 1,s = 0;
    if(l <= mid)s += query(i << 1 , l , r);
    if(r > mid)s += query(i << 1 | 1 , l , r);
    return s;
}
int n;
struct edge {
    int u,v;
    edge() {
        u = v = 0;
    }
}E[maxn];
vector<int> G[maxn];
void dfs1(int u,int fa) {
    sz[u] = 1;
    for(auto& v : G[u]) {
        if(v == fa)continue ;
        f[v] = u;
        d[v] = d[u] + 1;
        dfs1(v , u);
        sz[u] += sz[v];
        if(sz[v] > sz[son[u]])son[u] = v;
    }
    return ;
}
void dfs2(int u,int tp) {
    top[rk[id[u] = ++ cnt] = u] = tp;
    if(!son[u])return ;
    dfs2(son[u] , tp);
    for(auto& v : G[u]) {
        if(v == f[u]||v == son[u])continue ;
        dfs2(v , v);
    }
    return ;
}
int findans(int x,int y) {
    int ans = 0,tot = 0;
    while(top[x] != top[y]) {
        if(d[top[x]] < d[top[y]])swap(x , y);
        ans += query(1 , id[top[x]] , id[x]);
        tot += id[x] - id[top[x]] + 1;
        if(ans != tot)return -1;
        x = f[top[x]];
    }
    if(id[x] > id[y])swap(x , y);
    if(id[x] < id[y])ans += query(1 , id[x] + 1 , id[y]);
    tot += id[y] - id[x];
    return ans == tot ? ans : -1;
}
int main() {
    scanf("%d",&n);
    for(int i = 1;i < n;++ i) {
        scanf("%d %d",&E[i].u,&E[i].v);
        G[E[i].u].pb(E[i].v);
        G[E[i].v].pb(E[i].u);
    }
    dfs1(1 , 0);
    dfs2(1 , 1);
    build(1 , 1 , n);
    int Q;
    scanf("%d",&Q);
    while(Q --) {
        int op,x,y;
        scanf("%d %d",&op,&x);
        if(op == 1) {
            if(f[E[x].u] == E[x].v) {
                modify(1 , id[E[x].u] , 1);
            }
            else {
                modify(1 , id[E[x].v] , 1);
            }
        }
        else if(op == 2) {
            if(f[E[x].u] == E[x].v) {
                modify(1 , id[E[x].u] , 0);
            }
            else {
                modify(1 , id[E[x].v] , 0);
            }
        }
        else {
            scanf("%d",&y);
            printf("%d\n",findans(x , y));
        }
    }
    return 0;
}

[CF899F]Letters Removing

给定一个长度为 \(n\) 的,含 \(0\sim 9,a\sim z,A\sim Z\) 的字符串,\(m\) 次操作,每次删除一段区间内的所有为 \(c\) 的字符,输出删除完毕后的字符串。

串的总长度为 \(n\),则总的删除次数不超过 \(n\),这启发我们暴力操作。

std::setstd::vector 存下每种字符的位置,每次二分出位置暴力删除,总时间复杂度 \(O(m\log n)\)。

但有一个问题:删除操作会对后面的字符串产生影响,输入的区间并不一定是要删除的区间。

这是一个经典的问题,考虑用一个树状数组维护 \(1\sim n\) 的位置上是否有字符,然后用倍增+树状数组求出前缀和为 \(l,r\) 的第一个位置。

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

Code

// Problem: CF899F Letters Removing
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF899F
// Memory Limit: 250 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define Chtholly set<int>::iterator
using namespace std;
const int maxn = 2e5 + 5;
char t[maxn];
int n,m;
int c[maxn];
set<int> s[65];
int lev(char x) {
    if(x >= '0'&&x <= '9')return x - '0';
    if(x >= 'a'&&x <= 'z')return 10 + x - 'a';
    if(x >= 'A'&&x <= 'Z')return 36 + x - 'A';
}
int lowbit(int x) {
    return x & -x;
}
void add(int x,int y) {
    for(;x <= n;x += lowbit(x))c[x] += y;
    return ;
}
int query(int x) {
    int ans = 0,cnt = 0;
    for(int p = 17;~ p;-- p) {
        if(ans + (1 << p) <= n&&cnt + c[ans + (1 << p)] < x) {
            cnt += c[ans + (1 << p)];
            ans += 1 << p;
        }
    }
    return ans + 1;
}
int Query(int x) {
    int ans = 0;
    for(;x;x -= lowbit(x))ans += c[x];
    return ans;
}
int main() {
    scanf("%d %d",&n,&m);
    scanf("%s",t + 1);
    for(int i = 1;i <= n;++ i)add(i , 1),s[lev(t[i])].insert(i);
    for(int i = 1;i <= m;++ i) {
        int l,r;
        char c;
        scanf("%d %d %c",&l,&r,&c);
        l = query(l);
        r = query(r);
        if(s[lev(c)].empty())continue ;
        Chtholly itl = s[lev(c)].lower_bound(l),itr = s[lev(c)].upper_bound(r);
        for(Chtholly it = itl;it != itr;++ it)add(*it , -1);
        s[lev(c)].erase(itl , itr);
    }
    for(int i = 1;i <= n;++ i) {
        if(Query(i) - Query(i - 1) > 0)printf("%c",t[i]);
    }
    return 0;
}

[CF979D]Kuro and GCD and XOR and SUM

初始有一个空集合,\(Q\) 次操作。操作分两种:

  • 1 u:将 \(u\) 加入集合。

  • 2 x k s:求一个最大的 \(v\),使得:

  1. \(v + x \le s\)

  2. \(k\mid \gcd(v,x)\)

  3. \(x\oplus v\) 最大(\(\oplus\) 表示按位异或)

如果不存在这样的 \(v\),输出 \(-1\)。

\(1 \le Q,u,x,k,s \le 10^5\)

首先,\(k \mid \gcd(v,x)\) 显然可以转化为 \(k\mid v\land k\mid x\)。

因为要求的是一个值域内的异或最大值,显然要用到字典树。

综上,考虑对 \(1\sim V\)(\(V\) 表示值域)中的每个数建立一棵字典树。

插入操作就是把 \(u\) 插如所有 \(d(d\mid u)\) 对应的字典树。

查询时先判断是否存在 \(v\),若存在,则在 \(k\) 的字典树中查询。

由于 \(1\sim V\) 中约数个数约为 \(V\log V\),每个数的约数均摊下来是 \(\log V\) 个,故时空复杂度为 \(O(V\log^2 V)\)。

(CF 的数据没那么毒瘤,大概率是不会卡的。。吧)

// Problem: CF979D Kuro and GCD and XOR and SUM
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF979D
// Memory Limit: 500 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
using namespace std;
const int maxn = 1e5 + 5;
const int m = 1e5;//value
vector<int> G[maxn];
int rt[maxn],trie[maxn * 400][2],minv[maxn * 400],cnt;
void insert(int x,int y) {
    if(!rt[x])rt[x] = ++ cnt;
    int u = rt[x];
    minv[u] = min(minv[u] , y);
    for(int k = 17;~ k;-- k) {
        int c = y >> k & 1;
        if(!trie[u][c])trie[u][c] = ++ cnt;
        u = trie[u][c];
        minv[u] = min(minv[u] , y);
    }
    return ;
}
int query(int r,int x,int y) {
    int u = rt[r];
    if(minv[u] > y)return -1;
    for(int k = 17;~ k;-- k) {
        int c = x >> k & 1;
        c ^= 1;
        if(trie[u][c]&&minv[trie[u][c]] <= y)  {
            u = trie[u][c];
        }
        else {
            u = trie[u][c ^ 1];
        }
    }
    return minv[u];
}
int main() {
    for(int i = 1;i <= m;++ i) {
        for(int j = i;j <= m;j += i) {
            G[j].pb(i);
        }
    }
    memset(minv , 0x3f , sizeof(minv));
    int Q;
    scanf("%d",&Q);
    while(Q --) {
        int op,x,y,z;
        scanf("%d %d",&op,&x);
        if(op & 1) {
            for(auto& v : G[x]) {
                insert(v , x);
            }
        }
        else {
            scanf("%d %d",&y,&z);
            if(x % y) {
                puts("-1");
                continue ;
            }
            printf("%d\n",query(y , x , z - x));
        }
    }
    return 0;
}

美好的回忆:

真好玩哈哈(砸桌子)

[NOIP2020]字符串匹配

题解

这篇关于2022.8.3 颓废记录的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!