https://vjudge.net/problem/ZOJ-3963
题意:
给出一个数列,可以用这个数列构造一种二叉树,这个二叉树满足数的下标 i <= j,并且 si <= sj,si是sj的父亲,问给出的数列可以构造多少棵这样的二叉树。
思路:
这题赛上没有写出来,看了题解之后给补的。
首先,通过这题学到了,memset初始化数组有时是会造成超时的。set的upper_bound(x)这个函数,它返回set中大于x的第一个元素的位置(注意是大于,不是大于等于)。
于是,这题就是贪心加set。贪心指的是对于当前的输入的x,在前面找到小于等于它的数,如果说没有找到,那么就把这个数插入,作为一棵新的树的根。如果说找到了,那么就把这个点插入这棵树,并且把这个数可插入的数量减1,当数量为0的时候就不能再插入了。具体看看注释。
代码:
1 #include <stdio.h> 2 #include <string.h> 3 #include <vector> 4 #include <set> 5 #include <algorithm> 6 using namespace std; 7 8 int num,node[100005],cnt[100005];//node保存节点x的编号,cnt数组表示每个节点能插入的剩下的位置 9 10 vector<int> ans[100005];//ans保存答案 11 set<int> s; 12 13 void solve(int x,int pos) 14 { 15 set<int>::iterator it; 16 17 it = s.upper_bound(x); 18 19 if (it == s.begin())//x是最小的,所以选择插入 20 { 21 s.insert(x); 22 23 cnt[x] = 2;//一开始有两个空位 24 25 node[x] = num; 26 27 ans[node[x]].push_back(pos); 28 29 num++; 30 } 31 else 32 { 33 --it;//因为是大于x的位置,所以要-- 34 35 if (x == (*it))//相等,就不用插入,旧的元素 36 { 37 38 cnt[x]++;//相当于是加了2个位置,然后自己又占了一个 39 40 ans[node[x]].push_back(pos); 41 } 42 else 43 { 44 node[x] = node[*it]; 45 46 cnt[*it]--; 47 48 if (cnt[*it] == 0) s.erase(*it);//没有位置了,删除 49 50 cnt[x] = 2;//新的元素 51 52 s.insert(x);//后面插入是防止迭代器改变 53 54 ans[node[x]].push_back(pos); 55 } 56 } 57 } 58 int main() 59 { 60 int t; 61 62 scanf("%d",&t); 63 64 while (t--) 65 { 66 67 s.clear(); 68 69 num = 0; 70 71 int n; 72 73 scanf("%d",&n); 74 75 for (int i = 0;i <= n;i++) 76 { 77 node[i] = cnt[i] = 0; 78 ans[i].clear();//说了n的总和不超过2 * 10 ^ 6,用memeset反而会超时 79 } 80 81 for (int i = 1;i <= n;i++) 82 { 83 int x; 84 85 scanf("%d",&x); 86 87 solve(x,i); 88 } 89 90 printf("%d\n",num); 91 92 for (int i = 0;i < num;i++) 93 { 94 int sz = ans[i].size(); 95 96 printf("%d",sz); 97 98 for (int j = 0;j < sz;j++) 99 { 100 printf(" %d",ans[i][j]); 101 } 102 103 printf("\n"); 104 } 105 } 106 107 return 0; 108 }