codevs 1228(DFS序+线段树/树状数组)

本文介绍了一道竞赛题目,通过使用DFS序结合树状数组或线段树来解决区间查询问题。代码示例中包含了两种实现方式,一种是基于线段树的区间查询与修改操作,另一种是基于树状数组的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门
题解:大水题,跑一遍DFS序,树状数组/线段树维护区间和即可。终于,我这种万年线段树菜鸡也试着写BIT了,确实常数小了很多(•‾̑⌣‾̑•)✧˖°。

Segment Tree:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define root 1,1,n
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
#define pushup(rt) sum[rt]=sum[rt<<1]+sum[rt<<1|1]
const int MAXN=1e5+2;
int n,q;
int head[MAXN],edge=0,in[MAXN],out[MAXN],tim=0;
struct EDGE {
    int v,nxt;
}e[MAXN<<1];
int sum[MAXN<<2];
inline int read() {
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}
inline void adde(int u,int v) {
    e[edge].nxt=head[u],e[edge].v=v,head[u]=edge++;
    e[edge].nxt=head[v],e[edge].v=u,head[v]=edge++;
}
void dfs(int p,int fa) {
    in[p]=++tim;
    for (int i=head[p];~i;i=e[i].nxt) {
        int v=e[i].v;
        if (v^fa) dfs(v,p);
    }
    out[p]=tim;
}
void build(int rt,int l,int r) {
    if (l==r) {sum[rt]=1;return ;}
    int mid=(l+r)>>1;
    build(lson);
    build(rson);
    pushup(rt);
}
void modify(int rt,int l,int r,int pos) {
    if (l==r) {sum[rt]^=1;return ;}
    int mid=(l+r)>>1;
    if (pos<=mid) modify(lson,pos);
    else modify(rson,pos);
    pushup(rt);
}
int query(int rt,int l,int r,int L,int R) {
    if (L<=l&&r<=R) return sum[rt];
    int mid=(l+r)>>1,ret=0;
    if (L<=mid) ret+=query(lson,L,R);
    if (mid<R) ret+=query(rson,L,R);
    return ret;
}
int main() {
    memset(head,-1,sizeof(head));
    n=read();
    for (register int i=1;i<n;++i) {
        int u=read(),v=read();
        adde(u,v);
    }
    dfs(1,0);
    build(root);
    q=read();
    for (register int f=0;f<q;++f) {
        char ss;
        while (!isalpha(ss=getchar()));
        if (ss^'C') {
            int pos=read();
            printf("%d\n",query(root,in[pos],out[pos]));
        }
        else {
            int pos=read();
            modify(root,in[pos]);
        }
    }
    return 0;
}

Binary Index Tree:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=1e5+2;
int n,q;
int head[MAXN],edge=0,in[MAXN],out[MAXN],tim=0;
bool app[MAXN];
struct EDGE {
    int v,nxt;
}e[MAXN<<1];
int sum[MAXN];
inline int read() {
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}
inline void adde(int u,int v) {
    e[edge].nxt=head[u],e[edge].v=v,head[u]=edge++;
    e[edge].nxt=head[v],e[edge].v=u,head[v]=edge++;
}
void dfs(int p,int fa) {
    in[p]=++tim;
    for (int i=head[p];~i;i=e[i].nxt) {
        int v=e[i].v;
        if (v^fa) dfs(v,p);
    }
    out[p]=tim;
}
void modify(int p) {
    int v;
    if (app[p]) app[p]=0,v=-1;
    else app[p]=1,v=1;
    for (int i=p;i<=n;i+=(i&-i))
        sum[i]+=v;
}
int query(int p) {
    int ret=0;
    for (int i=p;i;i-=(i&-i))
        ret+=sum[i];
    return ret;
}
int main() {
    memset(head,-1,sizeof(head));
    memset(app,false,sizeof(app));
    n=read();
    for (register int i=1;i<n;++i) {
        int u=read(),v=read();
        adde(u,v);
        modify(i);
    }
    modify(n);
    dfs(1,0);
    q=read();
    for (register int f=0;f<q;++f) {
        char ss;
        while (!isalpha(ss=getchar()));
        if (ss^'C') {
            int pos=read();
            printf("%d\n",query(out[pos])-query(in[pos]-1));
        }
        else {
            int pos=read();
            modify(in[pos]);
        }
    }
    return 0;
}
### C++ 实现 r 个数的全排列算法 #### 算法概述 全排列问题是经典的回溯问题之一,通常通过深度优先搜索(DFS)来实现。对于给定长度为 `n` 的数组,从中选取 `r` 个数进行排列,可以通过递归的方式逐步构建每一种可能的结果。 以下是基于 DFS 和回溯方法的具体实现方式: --- #### 核心代码实现 ```cpp #include <iostream> using namespace std; const int MAX_N = 20; int n, r; // 数组大小和目标排列数量 bool used[MAX_N]; // 标记数组,记录当前数字是否已被使用 int result[MAX_N]; // 存储当前路径上的排列结果 int count = 0; // 记录找到的有效排列总数 void dfs(int depth) { if (depth == r) { // 如果已经达到所需排列的数量 r,则打印结果并返回 for (int i = 0; i < r; ++i) cout << result[i] << ' '; cout << endl; count++; return; } for (int i = 1; i <= n; ++i) { // 遍历可选的数字范围 [1,n] if (!used[i]) { // 若该数字未被使用 used[i] = true; // 将其标记为已使用 result[depth] = i; // 加入到当前排列中 dfs(depth + 1); // 继续处理下一个位置 used[i] = false; // 回溯:恢复状态以便尝试其他可能性 } } } int main() { cin >> n >> r; // 输入总数字量 n 和要排列的数量 r memset(used, false, sizeof(used)); // 初始化标记数组 dfs(0); // 开始深搜 cout << "Total permutations: " << count << endl; // 输出总的排列数目 return 0; } ``` --- #### 代码逻辑解析 上述代码实现了从 `[1, n]` 中选出 `r` 个数的所有排列情况。具体逻辑如下: 1. **初始化变量** 定义全局变量 `result[]` 来存储当前正在构建的排列列;定义布尔型数组 `used[]` 表示某个数字是否已经被加入排列[^3]。 2. **核心函数 `dfs()`** 使用递归来模拟深度优先搜索的过程。参数 `depth` 表示当前填充的位置索引: - 当前层的任务是从尚未使用的数字集合中挑选一个合适的数字放入当前位置; - 对于每一个可用的选择,将其标记为已使用 (`used[i] = true`) 并继续向下一层递归调用; - 在完成一次完整的排列后,撤销本次操作的影响(即设置 `used[i] = false`),从而允许后续分支重新利用这个数字[^4]。 3. **终止条件** 当到达第 `r` 层时停止递归,并输出当前形成的排列组合[^2]。 4. **输入与执行流程** 用户需提供两个整数值——分别是候选集规模 `n` 及期望抽取的子集大小 `r`。程会依次枚举所有满足条件的情况直至穷尽全部选项。 --- #### 时间复杂度分析 假设我们需要计算的是 P(n,r),即从 n 个不同元素里取出 r 个的不同顺安排方案数,则理论上最多会有 O(P(n,r)) 种不同的排列形式待考察。由于每次决策都需要扫描剩余候补列表中的每一项,因此实际运行时间大致相当于 ∑_{k=0}^{r−1}(n-k)=O(r⋅n)[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值