poj 2182 入门题

来源:互联网 发布:mac打开u盘隐藏文件 编辑:程序博客网 时间:2024/05/18 02:57

poj 2182链接

题意: 

你有N头牛,每头牛有编号,不过顺序是打乱的,现在一某种位置排好了序,告诉你第二头牛到第N头牛前面有几头牛编号比自己小,求该序列下每头牛的编号。

这一题是不是怎么也想不到用线段树做?其实这个题可以用普通方法暴力做,不过线段树能提高速度,我先给出暴力法代码,其实看懂了暴力法就能明白怎么用线段树了。

  暴力1:

#include<stdio.h>int a[10005],vis[100005];int ans[100005];int main(){int n,i,j,t;scanf("%d",&n);for(i=2;i<=n;i++)scanf("%d",&a[i]);a[1]=0;for(i=n;i>=1;i--){t=a[i]+1;for(j=1;j<=t;j++){if(vis[j])t++;}ans[i]=t;vis[t]=1;}for(i=1;i<=n;i++)printf("%d\n",ans[i]);}

暴力2:(用链表优化)

#include<stdio.h>int after[10000];int t[10000],ans[10000];int ss(int key){int temp=0,pre;for(int i=0;i<=key;i++){pre=temp;temp=after[temp];}after[pre]=after[temp];return temp;}int main(){int n,i;scanf("%d",&n);for(i=0;i<=n;i++)after[i]=i+1;for(i=2;i<=n;i++)scanf("%d",&t[i]);for(i=n;i>=2;i--)ans[i]=ss(t[i]);ans[1]=after[0];for(i=1;i<=n;i++)printf("%d\n",ans[i]);}

线段树:

每个节点tree[node]记录该区间有几个没被用过的数,每次确定完一头牛编号就修改线段树,每次查询tree[node]节点的右区间就是答案的编号。

#include<stdio.h>int tree[30000];int a[10000];int num[10000];void build(int l,int r,int node){if(l==r){tree[node]=1;return;}int m=(l+r)/2;build(l,m,node*2);build(m+1,r,node*2+1);tree[node]=tree[node*2]+tree[node*2+1];}int query(int l,int r,int node,int sum){int m=(l+r)/2;if(l==r)return r;else if(tree[node*2]>=sum)return query(l,m,node*2,sum);else return query(m+1,r,node*2+1,sum-tree[node*2]);}void update(int node,int l,int r,int pos){if(l==r){tree[node]--;return;}int m=(l+r)/2;if(m>=pos)update(node*2,l,m,pos);else update(node*2+1,m+1,r,pos);tree[node]=tree[node*2]+tree[node*2+1];}int main(){int n,i;scanf("%d",&n);build(1,n,1);a[1]=0;for(i=2;i<=n;i++)scanf("%d",&a[i]);for(i=n;i>=1;i--){num[i]=query(1,n,1,a[i]+1);update(1,1,n,num[i]);}for(i=1;i<=n;i++)printf("%d\n",num[i]);}