问题

有一个专门为了集合运算而设计的“集合栈”计算机。该机器有一个初始为空的栈,并且支持以下操作:

PUSH:空集“{}”入栈

DUP:把当前栈顶元素复制一份后再入栈

UNION:出栈两个集合,然后把两者的并集入栈

INTERSECT:出栈两个集合,然后把二者的交集入栈

ADD:出栈两个集合,然后把先出栈的集合加入到后出栈的集合中,把结果入栈

  1. 每次操作后,输出栈顶集合的大小(即元素个数)。例如栈顶元素是A={ {}, {{}} }, 下一个元素是B={ {}, {{{}}} },则:<br />UNION操作将得到{ {}, {{}}, {{{}}} },输出3.

INTERSECT操作将得到{ {} },输出1

ADD操作将得到{ {}, {{{}}}, { {}, {{}} } },输出3.

输入不超过2000个操作

Sample Input

  1. 9
  2. PUSH
  3. DUP
  4. ADD
  5. PUSH
  6. ADD
  7. DUP
  8. ADD
  9. DUP
  10. UNION

Sample Output

  1. 0
  2. 0
  3. 1
  4. 0
  5. 1
  6. 1
  7. 2
  8. 2
  9. 2

题解

首先可以确定要用到数据结构栈,然后观察这个栈的特点: 存放的是集合,而集合里面存放的也是集合。

所以这个栈的结构应该是这样的: stack<set<set<>,set<>>,set<>>

然而,这种结构明显是不科学的,它像一个无穷递归一样,永无止境。

观察到,栈只是用来存放集合的地方,而集合的操作与栈没有关联。

于是我们可以把地址和数据分离:将集合映射为“地址”(即整数ID),每次取出(即与位置有关)通过ID访问集合,再对集合单独操作。

这让我们要用两种映射:

  • 从集合映射到ID:map<set<int>,int> idData // 存放地址
  • 从ID映射到集合:map<int,set<int>> setData // 存放数据
    然而ID符合0~n,所以可以用vector<set<int>>实现。

这也是模拟链表的思想。

代码

  1. /*
  2. 栈 模拟链表
  3. */
  4. #include <bits/stdc++.h>
  5. #define ALL(x) x.begin(),x.end()
  6. #define INS(x) inserter(x,x.begin())
  7. using namespace std;
  8. typedef set<int> Set;
  9. map<Set,int> idData;
  10. vector<Set> setData;// map<int,Set> SetData
  11. int ID(Set x)
  12. {
  13. if(idData.count(x)) return idData[x];
  14. setData.push_back(x);
  15. return idData[x] = setData.size() - 1;
  16. }
  17. int main()
  18. {
  19. int n,m;
  20. char ord[100];
  21. scanf("%d",&n);
  22. while(n--)
  23. {
  24. scanf("%d",&m);
  25. stack<int> st;
  26. while(m--)
  27. {
  28. scanf("%s",&ord);
  29. if(ord[0] == 'P') st.push(ID(Set()));
  30. else if(ord[0] == 'D') st.push(st.top());
  31. else
  32. {
  33. Set x1 = setData[st.top()];
  34. st.pop();
  35. Set x2 = setData[st.top()];
  36. st.pop();
  37. Set x;
  38. if(ord[0] == 'U') set_union(ALL(x1),ALL(x2),INS(x));
  39. else if(ord[0] == 'I') set_intersection(ALL(x1),ALL(x2),INS(x));
  40. else if(ord[0] == 'A')
  41. {
  42. x = x2;
  43. x.insert(ID(x1));
  44. }
  45. st.push(ID(x));
  46. }
  47. printf("%d\n",setData[st.top()].size());
  48. }
  49. printf("***\n");
  50. }
  51. return 0;
  52. }