题目链接
https://zoj.pintia.cn/problem-sets/91827364500/problems/91827370090
题意:
给你一个长度为n的序列,然后让你求出把这个序列分成最少个数的"堆子序列",请输出"堆子序列"的个数和方案。
堆子序列:一棵n个节点的二叉树,对于一个非根节点,节点的值小于等于儿子节点的值,且下标小于儿子节点的下标。
题解:
不难想到解法就是从前往后枚举,并维护一个存储不同树对应节点的结构,然后贪心的把当前数插入到最大的小于当前值的节点的下方,如果没有的话,就证明这是一棵新树。
关键就在于怎么维护这些树和节点,暴力的话时间复杂度为n^2,肯定不行。
考虑用set维护这个节点序列就行,这样可以利用set自带的upper_bound实现log级别找到要插入的位置。
trick:因为是二叉树,不要忘记一个节点的有两个儿子时从set删掉。
代码实现:
#include <bits/stdc++.h> using namespace std; const int N = 1e5+7; int a[N]; struct node{ int ind,pos; friend bool operator<(node A,node B){ return a[A.pos]==a[B.pos]?A.pos<B.pos:a[A.pos]<a[B.pos]; } }; set<node> s;//维护节点序列 vector<int> v[N];//维护每一棵树对于的节点 int deg[N]; int main(){ int T;scanf("%d",&T); while(T--){ int n;scanf("%d",&n); s.clear(); // memset(deg,0,sizeof deg); for(int i=1;i<=n;i++) deg[i]=0;//不能用memset,会超时 node tmp; int cnt=0;//记录树的个数 for(int i=1;i<=n;i++){ scanf("%d",&a[i]); tmp.pos=i; set<node>::iterator it=s.upper_bound(tmp); if(it==s.begin()){//建一棵新树 v[cnt].push_back(tmp.pos); tmp.ind=cnt;tmp.pos=i; s.insert(tmp); cnt++; } else{//找到对应位置 it--; tmp=*it; deg[tmp.pos]++; if(deg[tmp.pos]==2) s.erase(it);//已经有两个儿子,删除当前节点 tmp.pos=i; s.insert(tmp); v[tmp.ind].push_back(i); } } printf("%d\n",cnt); for(int i=0;i<cnt;i++){ int len=v[i].size(); printf("%d",len); for(int j=0;j<len;j++){ printf(" %d",v[i][j]); } printf("\n"); v[i].clear(); } } return 0; } /* 4 4 1 2 3 4 4 2 4 3 1 4 1 1 1 1 5 3 2 1 4 1 */