Apple Tree 有时间戳的树状数组

Problem Description

There is an apple tree outside of kaka's house. Every autumn, a lot of apples will grow in the tree. Kaka likes apple very much, so he has been carefully nurturing the big apple tree.

The tree has N forks which are connected by branches. Kaka numbers the forks by 1 to N and the root is always numbered by 1. Apples will grow on the forks and two apple won't grow on the same fork. kaka wants to know how many apples are there in a sub-tree, for his study of the produce ability of the apple tree.

The trouble is that a new apple may grow on an empty fork some time and kaka may pick an apple from the tree for his dessert. Can you help kaka?

 

 

Input

The first line contains an integer N (N ≤ 100,000) , which is the number of the forks in the tree.
The following N - 1 lines each contain two integers u and v, which means fork u and fork v are connected by a branch.
The next line contains an integer M (M ≤ 100,000).
The following M lines each contain a message which is either
"C x" which means the existence of the apple on fork x has been changed. i.e. if there is an apple on the fork, then Kaka pick it; otherwise a new apple has grown on the empty fork.
or
"Q x" which means an inquiry for the number of apples in the sub-tree above the fork x, including the apple (if exists) on the fork x
Note the tree is full of apples at the beginning

 

 

Output
For every inquiry, output the correspond answer per line.
 

 

Sample Input
3 1 2 1 3 3 Q 1 C 2 Q 1
 

 

Sample Output
3 2
**********************************************************************************************************************************
最重要的是记录树状数组的时间戳
**********************************************************************************************************************************
  1 /*
  2  有时间戳的树状数组,
  3 */
  4 #include<iostream>
  5 #include<string>
  6 #include<cstring>
  7 #include<cmath>
  8 #include<queue>
  9 #include<cstdio>
 10 #include<map>
 11 #include<algorithm>
 12 using namespace std;
 13 #define  maxn  220000
 14 typedef vector<int> VCT_INT;
 15 vector<VCT_INT>G(maxn/2);
 16 int nCount,n,m;
 17 int Start[maxn],End[maxn];
 18 int C[maxn],lowbit[maxn];
 19 int HasApple[maxn/2];
 20  int query_sum(int p)//求一个区间内的和
 21  {
 22     int sum=0;
 23     while(p>0)
 24     {
 25        sum+=C[p];
 26        p-=lowbit[p];
 27     }
 28     return sum;
 29  }
 30 
 31  void modify(int p,int val)//插入更新
 32  {
 33      while(p<=nCount)
 34      {
 35         C[p]+=val;
 36         p+=lowbit[p];
 37      }
 38  }
 39 
 40 void dfs(int s)//插入时间戳
 41 {
 42    Start[s]=++nCount;
 43    for(int it=0;it<G[s].size();it++)
 44     {
 45        dfs(G[s][it]);
 46     }
 47     End[s]=++nCount;
 48 }
 49 
 50 int main()
 51 {
 52    int i,j,k;
 53    while(scanf("%d",&n)!=EOF)
 54    {
 55       int a,b,c;
 56      for(i=1;i<n;i++)
 57      {
 58         scanf("%d %d",&a,&b);
 59         G[a].push_back(b);
 60      }
 61      nCount=0;
 62      dfs(1);
 63      for(i=1;i<=nCount;i++)
 64       lowbit[i]=i&(i^(i-1));
 65      for(i=1;i<=nCount;i++)//求C[]数组
 66       C[i]=i-(i-lowbit[i]+1)+1;
 67      for(i=1;i<=n;i++)
 68      {
 69        HasApple[i]=1;
 70      }
 71       scanf("%d",&m);
 72     for(i=0;i<m;i++)
 73     {
 74         char cmd[10];
 75         int a;
 76         scanf("%s%d",cmd,&a);
 77         if(cmd[0]=='C')
 78         {
 79             if(HasApple[a])
 80             {
 81                 modify(Start[a],-1);
 82                 modify(End[a],-1);
 83                 HasApple[a]=0;
 84             }
 85             else
 86             {
 87                 modify(Start[a],1);
 88                 modify(End[a],1);
 89                 HasApple[a]=1;
 90             }
 91         }
 92         else
 93         {
 94             int t1=query_sum(End[a]);
 95             int t2=query_sum(Start[a]);
 96             printf("%d\n",(t1-t2)/2+HasApple[a]);
 97         }
 98     }
 99    }
100 }
View Code

 

转载于:https://www.cnblogs.com/sdau--codeants/p/3484874.html

<think>好的,我现在需要回答用户的问题:在C++中,使用哪个STL容器进行键值对的查找最快。用户可能希望了解不同容器的性能差异,特别是查找操作的效率。我需要回忆C++中的相关容器,它们的底层实现以及时间复杂度。 首先,STL中常用的键值对容器有std::map、std::unordered_map,还有C++11引入的std::unordered_map。另外,有时候可能也会考虑其他结构比如std::vector配合pair,但查找效率可能不高。 std::map是基于红黑树实现的,这是一种平衡二叉搜索树,因此它的查找、插入和删除操作的时间复杂度都是O(log n),其中n是元素的数量。红黑树保证了元素的有序性,按关键字顺序遍历时会得到排序后的结果。 而std::unordered_map则是基于哈希表实现的。理想情况下,哈希表的查找、插入和删除操作的平均时间复杂度是O(1),最坏情况下是O(n),但这种情况很少发生,尤其是在良好的哈希函数和适当的桶大小的情况下。不过,哈希表中的元素是无序的。 所以,从时间复杂度来看,std::unordered_map的平均情况下的查找速度应该比std::map快,尤其是在数据量较大的时候,因为O(1)比O(log n)要快。但是需要考虑到哈希冲突的问题,如果哈希函数设计不好,导致很多冲突,可能会影响性能。 另外,用户可能还关心内存使用情况或者是否需要有序存储。如果不需要有序,unordered_map更合适;如果需要有序遍历,map是更好的选择。但问题中只提到查找速度,所以主要比较两者的查找效率。 还需要注意,C++标准库中的容器具体实现可能因编译器而异,但一般情况下,哈希表的平均O(1)确实更优。例如,当键是整数或字符串时,哈希函数通常效率较高,冲突较少,查找速度更快。 可能的例外情况是当数据量非常小的时候,比如几个元素,这时O(log n)和O(1)可能差别不大,甚至可能由于常数因子较小,红黑树更快。但这种情况较少,通常讨论的是数据量较大的情况。 总结:对于纯查找操作,尤其是在平均情况下,std::unordered_map是最快的STL容器,因为它基于哈希表,时间复杂度为O(1)。而std::map的查找是O(log n)。不过,需要根据具体情况选择合适的容器,比如是否需要有序、哈希函数的质量等。</think>在C++中,**`std::unordered_map`** 是用于键值对查找最快的STL容器,其查找操作的平均时间复杂度为 **O(1)**(常数时间)。这是因为它基于**哈希表**实现,通过哈希函数直接定位键的存储位置,无需遍历或树形结构的层级搜索。 ### 关键对比: 1. **`std::unordered_map`(哈希表)**: - **查找速度**:平均 O(1),最坏 O(n)(哈希冲突严重时)。 - **特点**:元素无序,依赖高效的哈希函数。 - **适用场景**:需要高频查找且无需有序遍历键值对的场景(如缓存、字典)。 2. **`std::map`(红黑树)**: - **查找速度**:稳定 O(log n)。 - **特点**:元素按键有序(默认升序),适合需要有序性的场景。 - **适用场景**:需按顺序遍历或范围查询(如按时间戳排序的日志)。 ### 性能示例: - 若存储 **10^6 个键值对**,`std::unordered_map` 的查找通常比 `std::map` 快数十倍(哈希冲突少时)。 - 但若哈希函数设计不佳(如大量碰撞),性能可能退化为链表查询(O(n))。 ### 选择建议: - **优先 `std::unordered_map`**:当键类型(如 `int`、`std::string`)已有高质量哈希函数,且无需有序性时。 - **选择 `std::map`**:若需要有序遍历键,或键类型难以哈希(需自定义高质量哈希函数避免冲突)。 ### 代码示例: ```cpp #include <unordered_map> #include <map> #include <string> int main() { // 哈希表实现:最快查找 std::unordered_map<std::string, int> hash_map = {{"apple", 5}, {"banana", 3}}; int val = hash_map["apple"]; // O(1) 查找 // 红黑树实现:有序但稍慢 std::map<std::string, int> tree_map = {{"apple", 5}, {"banana", 3}}; val = tree_map["banana"]; // O(log n) 查找 return 0; } ``` ### 总结: 若追求极致查找速度且无需顺序,**`std::unordered_map`** 是最优选择;若需有序性,则用 **`std::map`**。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值