HDU 4358 Boring counting(树的遍历+树状数组+离散化+离线处理)
来源:互联网 发布:java导出excel到前端 编辑:程序博客网 时间:2024/05/21 22:48
HDU 4358 Boring counting(树的遍历+树状数组+离散化+离线处理)
分析:
首先读入所有的节点权重存在nodes[n]中,然后读入n-1条边,1为根节点.把整个图做成一个邻接表的形式.用vector<int> g[n],其中g[i]表示与i邻接的点都有哪些.然后对1号节点执行中序遍历.
首先本题是对整棵子树的查询,其实我们可以对该树执行一次后序遍历,把我们访问到的节点编号一一保存到nodes[i].index=j中 (i是这个节点原来的编号,j是表示我们中序遍历这棵树时,访问该点时是第j个访问的).
现在每个根节点的查询其实就正好对应了一个连续的区间查询.(想想是不是)并把每个根节点所对应的区间L[i]=j1和R[i]=j2记下来. (j1和j2是节点新的编号,是在执行初始i节点的后序遍历后,所有节点重新编号后,查询的区间是[j1,j2])
后序遍历后,给所有节点的权重重新编号后存在了a[i]中,我们如果要查询初始x节点的子树,只需要查询a[i]数组内在区间[L[x],R[x]]中符合要求的值有多少个.
接下来我们首先把a[i]的值重新映射集中(因为a[i]最大可能10亿,但却只有10W个a[i]值)到数组b[i]中.
离线处理每个查询,把每个查询按L从小到大排序.
预处理:接下来我们建一棵树状数组A[n],然后从1到n扫描b[i]数组,如果对于b[i]=x的值是正好第k次出现了,我们就add(i,1),且我们需要找到b[i]值正好出现了第k+1次的位置y,执行add(y,-1).扫描完b[n]之后,我们保证sum(R)的值就是对所有区间[1,R]的查询结果.
接下来我们需要查询所有[2,R]区间的结果,我们首先要消除b[1]对后续数列产生的影响,我们找到b[1]值第k次出现的位置y1和第k+1次出现的位置y2和第k+2次出现的位置y3(其实就是上一次针对b[1]的值执行add(,1)和add(,-1)的位置),所以我们执行add(y1,-1)和add(y2,2)和add(y3,-1). 消除影响后,b[1]就好像从来没有出现过一样.
完成上面那步,我们保证sum(R)的值就是对所有区间[2,R]的查询结果.
当我们查询完sum(R)表示区间[i,R]的查询结果后,我们需要找到当前d[i]值从i位置开始第k次出现的位置y1(包括i,且d[i]就算是第一次出现的位置),和d[i]值第k+1次出现的位置y2,和d[i]值第k+2次出现的位置y3,执行add(y1,-1) 和 add(y2,2) 和add(y3,-1)
原始代码:超时
#include<cstdio>#include<cstring>#include<algorithm>#include<vector>using namespace std;const int MAXN =100000+100;const int MAXM =100000+100;struct node{ int v; int index; bool operator <(const node&b)const { return v<b.v; }}nodes[MAXN],a[MAXN];//初始树的权值struct command{ int l,r; int index; bool operator <(const command&b)const { return l<b.l; }}coms[MAXM];//查询int L[MAXN],R[MAXN],cnt;int b[MAXN];int ans[MAXM];int cur[MAXN];//cur[i]表示b[j]=i值当前处理阶段第k次出现的位置int first[2*MAXN],next[2*MAXN],u[2*MAXN],v[2*MAXN];void add(int i,int j,int num){ u[num]=i; v[num]=j; next[num]=first[u[num]]; first[i]=num;}int dfs(int fa,int a)//后序访问以u为根节点(fa是u的父节点)的树,并返回该树的最最左下角的点的新编号{ int min_num=cnt; for(int e=first[a];e!=-1;e=next[e])if(v[e]!=fa) { dfs(a,v[e]); } L[a]=min_num; R[a]=cnt; nodes[a].index=cnt++; return L[a];}struct HASHMAP{ int head[MAXN]; int next[MAXN];//把所有相同的b[i]值从前到后串联起来 void init() { memset(head,-1,sizeof(head)); } int insert(int i,int v) { int num=1;//表示插入的这个b[i]是值为v的第num次出现了 next[i]=-1; if(head[v]==-1) { head[v]=i; } else { int j=head[v]; num++; while(next[j]!=-1) { j=next[j]; num++; } next[j]=i; } return num; } int find(int i)//找到b[i]值相同的后继元素的下标 { if(i==-1) return -1; return next[i]; }}hm;int c[MAXN];int lowbit(int x){ return x&(-x);}int sum(int x){ int res=0; while(x) { res +=c[x]; x-=lowbit(x); } return res;}void add(int x,int v){ while(x<MAXN) { c[x]+=v; x+=lowbit(x); }}int main(){ int T,kase=1; scanf("%d",&T); while(T--) { printf("Case #%d:\n",kase++); int n,K; scanf("%d%d",&n,&K); for(int i=1;i<=n;i++)//读权重 { scanf("%d",&nodes[i].v); } memset(first,-1,sizeof(first)); for(int i=1;i<n;i++)//读边 { int u,v; scanf("%d%d",&u,&v); add(u,v,i*2-1); add(v,u,i*2); } cnt=1;//记录当前递归到了第几个点 dfs(-1,1); for(int i=1;i<=n;i++) { a[nodes[i].index].v = nodes[i].v; a[nodes[i].index].index = nodes[i].index; }/* for(int i=1;i<=n;i++) { printf("%d %d %d\n",a[nodes[i].index],L[i],R[i]); }*/ sort(a+1,a+n+1); int max_num=1; b[a[1].index]=1; for(int i=2;i<=n;i++) { if(a[i].v==a[i-1].v) b[a[i].index]=b[a[i-1].index]; else b[a[i].index]=++max_num; } hm.init(); memset(cur,-1,sizeof(cur)); for(int i=1;i<=n;i++) { int num = hm.insert(i,b[i]); if(num==K) cur[b[i]]=i;//b[i]值第K次出现的位置是i }/* for(int i=1;i<=n;i++) printf("%d ",b[i]); printf("\n"); for(int i=1;i<=n;i++) { printf("%d %d\n",L[i],R[i]); } printf("***************\n");*/ int Q; scanf("%d",&Q); for(int i=1;i<=Q;i++) { int root; scanf("%d",&root); coms[i].l=L[root]; coms[i].r=R[root]; coms[i].index=i; } sort(coms+1,coms+Q+1); for(int i=1;i<=n;i++)//预处理 { int y1=cur[i]; int y2=hm.find(y1); if(y1>-1) { add(y1,1); if(y2>-1) add(y2,-1); } } int j=1;//表示当前处理排序后的第j条命令 for(int i=1;i<=n;i++) { while(coms[j].l==i) { ans[coms[j].index] = sum(coms[j].r); j++;//别忘了 } if(j>Q) break; int y1=cur[b[i]]; int y2=hm.find(y1); int y3=hm.find(y2); if(y1>0) { add(y1,-1); if(y2>0) { add(y2,2); if(y3>0) { add(y3,-1); } } } cur[b[i]]=y2;//这一句别忘了,更新d[i]下一个第k次出现的位置 } for(int i=1;i<=Q;i++) printf("%d\n",ans[i]); } return 0;}
现在换另一种做法,前面几步都一样,知道预处理那里,现在不预处理了.而是将所有询问按照R从小到大排序(如果R相同,则按L从小到大排序).
我们从1到n读入每一个b[i]的值,并且用一个vector<int> vec[MAXN]记录vec[b[i]][k]=j表示b[i]值已经出现了k次且第k次出现在j位置.
如果当前size = vec[b[i]].size()-1正好等于k(因为vec[x][0]=0,顶掉了这个0位,所以多存了一个0),那么执行add( vec[b[i]][size-k]+1,1 ); 和add( vec[b[i]][size-k+1]+1,-1 );
此时我们保证sum(L)就是所有区间[L,i]的查询值(其中L是可变的,但是i是固定的也就是说只有当前查询存在R==i时才查询,如果没有R==i,就继续读入下一个。)
如果当前size = vec[b[i]].size()-1正好大于k(因为vec[x][0]=0,顶掉了这个0位,所以多存了一个0),那么执行add( vec[b[i]][size-k-1]+1,-1 ); 和add( vec[b[i]][size-k]+1,2 ); 和 add( vec[b[i]][size-k+1]+1,-1 );
(自己在数轴上画图验证一下看看是不是)
1.下次如果需要离散化的数据,尽量先进行离散化在做其他操作。
2.由于没有初始化c数组,导致WA1小时,真是郁闷。以后全局变量和数组都在最前面统一初始化。
3.如果栈可能溢出,记得栈中的变量尽量少就不会溢出,否则就用自己手写的栈。
AC代码:765ms。
#include<cstdio>#include<cstring>#include<algorithm>#include<vector>using namespace std;//HDU开栈外挂,本代码不用也可以AC#pragma comment(linker, "/STACK:102400000,102400000")const int MAXN =100000+5000;const int MAXM =100000+5000;vector<int> vec[MAXN];struct node{ int v; int index; bool operator <(const node&b)const { return v<b.v; }} nodes[MAXN],a[MAXN]; //初始树的权值struct command{ int l,r; int index; bool operator <(const command&b)const { return r<b.r ; }} coms[MAXM]; //查询int L[MAXN],R[MAXN],cnt;int b[MAXN];int ans[MAXM];int cur[MAXN];//cur[i]表示b[j]=i值当前处理阶段第k次出现的位置int first[2*MAXN],next[2*MAXN],u[2*MAXN],v[2*MAXN];void add(int i,int j,int num){ u[num]=i; v[num]=j; next[num]=first[u[num]]; first[i]=num;}void dfs(int fa,int a)//后序访问以u为根节点(fa是u的父节点)的树,并返回该树的最最左下角的点的新编号{ L[a]=cnt; for(int e=first[a]; e!=-1; e=next[e])if(v[e]!=fa) { dfs(a,v[e]); } R[a]=cnt; nodes[a].index=cnt++;}int c[MAXN];int lowbit(int x){ return x&(-x);}int sum(int x){ int res=0; while(x) { res +=c[x]; x-=lowbit(x); } return res;}void add(int x,int v){ while(x<MAXN) { c[x]+=v; x+=lowbit(x); }}int main(){ int T; scanf("%d",&T); for(int kase=1; kase<=T; kase++) { memset(first,-1,sizeof(first)); memset(c,0,sizeof(c)); cnt=1;//记录当前递归到了第几个点 int n,K; scanf("%d%d",&n,&K); for(int i=1; i<=n; i++) //读权重 { scanf("%d",&nodes[i].v); } for(int i=1; i<n; i++) //读边 { int u,v; scanf("%d%d",&u,&v); add(u,v,i*2-1); add(v,u,i*2); } dfs(-1,1); for(int i=1; i<=n; i++) { a[nodes[i].index].v = nodes[i].v; a[nodes[i].index].index = nodes[i].index; } sort(a+1,a+n+1); int max_num=1; b[a[1].index]=1; for(int i=2; i<=n; i++) { if(a[i].v==a[i-1].v) b[a[i].index]=b[a[i-1].index]; else b[a[i].index]=++max_num; } int Q; scanf("%d",&Q); for(int i=1; i<=Q; i++) { int root; scanf("%d",&root); coms[i].l=L[root]; coms[i].r=R[root]; coms[i].index=i; } sort(coms+1,coms+Q+1); for(int i=1; i<=n; i++) { vec[i].clear(); vec[i].push_back(0); } int j=1;//表示当前处理排序后的第j条命令 for(int i=1; i<=n; i++) { vec[b[i]].push_back(i); int size = vec[b[i]].size()-1; if(size>=K) { if(size==K) { add( vec[b[i]][size-K]+1,1 ); add( vec[b[i]][size-K+1]+1,-1 ); } else if(size>K) { add( vec[b[i]][size-K-1]+1,-1 ); add( vec[b[i]][size-K]+1,2 ); add( vec[b[i]][size-K+1]+1,-1 ); } } while(coms[j].r==i && j<=Q) { ans[coms[j].index] = sum( coms[j].l ); j++; } if(j>Q) break; } printf("Case #%d:\n",kase); for(int i=1; i<=Q; i++) printf("%d\n",ans[i]); if(kase<T) printf("\n"); } return 0;}
- HDU 4358 Boring counting(树的遍历+树状数组+离散化+离线处理)
- sdut2610---Boring Counting(离线+树状数组+离散化)
- HDU 4358 Boring counting(离线 + 树状数组)
- HDU 4358-I - Boring counting-dfs序+离线+树状数组/线段树
- HDU 4358 Boring counting (树状数组)
- HDU 4358 Boring counting(树状数组)
- HDU Boring counting 树状数组
- HDU 4358 Boring counting(树状数组离线操作+欧拉序列)
- hdu 5862 Counting Intersections 【线段树/树状数组+离散化】
- HDU 3333 树状数组(线段树) + 离散化 + 离线处理
- hdu 5324 Boring Class cdq分治+树状数组+离散化
- hdu 4358 Boring counting 线段树离线操作
- HDU 3450 Counting Sequences(树状数组+DP+离散化)
- hdu 3450 Counting Sequences(树状数组+排序离散化)
- hdu 5682 Counting Intersections 离散化+树状数组
- hdu 5862 Counting Intersections 坐标离散化+树状数组
- HDU 5862 Counting Intersections(离散化+树状数组)
- HDU 4417 —— Super Mario(树状数组,离散化,离线处理)
- IT不难
- AUL(MyDUL) Oracle及Oracle ASM数据恢复
- struts架构
- Eclipse断点调试
- tolower
- HDU 4358 Boring counting(树的遍历+树状数组+离散化+离线处理)
- VS2010调试技巧
- java--构造方法,构造代码块,静态代码块区别及代码示例
- Provider,ORMLite,GreenDao的实现,并且简单性能对比
- oracle常用SQL归纳
- JDBC
- Createprocess遇到的问题
- 九度OnlineJudge题目1015:还是A+B
- 计算机网络(2)物理层、数据链路层