【Splay】BZOJ 1588 [HNOI2002]营业额统计

来源:互联网 发布:办公网络 编辑:程序博客网 时间:2024/05/16 07:18
用splay的基本操作Find_pre和Find_succ来实现
</pre><pre name="code" class="cpp">
#include<cstdio>#include<iostream>#include<cmath>using namespace std;const int maxint = 214748364;const int MAX_N = 40000;//0表示空结点NULLstruct Node{int key,num,fa;int son[2];}Tree[MAX_N];int root=0,tot=0;int n;//c=0 为左旋,c=1 为右旋void Rotate(int x,int c){int y = Tree[x].fa;Tree[y].son[!c] = Tree[x].son[c];if(Tree[x].son[c]) Tree[Tree[x].son[c]].fa = y;Tree[x].fa = Tree[y].fa;if(Tree[y].fa){/*if(Tree[y].fa.son[0] == y)Tree[y].fa.son[0] = x;else Tree[y].fa.son[1] = x;*/Tree[Tree[y].fa].son[Tree[Tree[y].fa].son[1]==y] = x;}Tree[y].fa = x;Tree[x].son[c] = y;}void Splay(int x, int f){while(Tree[x].fa != f){if(Tree[Tree[x].fa].fa == f)Rotate(x,Tree[Tree[x].fa].son[0]==x);else{int y = Tree[x].fa;int kind = Tree[Tree[y].fa].son[1] == y; //kind=0 表示y为其父亲的左儿子,kind=1 表示y为右儿子if(Tree[y].son[kind] == x)Rotate(y,!kind) , Rotate(x,!kind);elseRotate(x,kind)  , Rotate(x,!kind);}}if(f==0) root = x;}void Insert(int val,int x){//根节点为0,即树为空if(!x){root = ++tot;Tree[tot].key = val;Tree[tot].fa = 0;Tree[tot].num = 1;return ;}if(Tree[x].key == val){Tree[x].num++;Splay(x,0);//将当前结点旋转到NULL下,即成为根节点return;}//要插入的结点为空if(!Tree[x].son[val>Tree[x].key]){Tree[x].son[val>Tree[x].key] = ++tot;Tree[tot].fa = x;Tree[tot].key = val; Tree[tot].num = 1;Splay(tot,0);}else Insert(val,Tree[x].son[val>Tree[x].key]);}int Find_pre(int x){if(Tree[x].num>1) return Tree[x].key;int Next = Tree[x].son[0];if(!Next) return maxint; //没有前驱while(Tree[Next].son[1]) Next = Tree[Next].son[1];return Tree[Next].key;}int Find_succ(int x){if(Tree[x].num>1) return Tree[x].key;int Next=Tree[x].son[1];if(!Next) return maxint;//没有后继while(Tree[Next].son[0]) Next = Tree[Next].son[0];return Tree[Next].key;}void middle_order(int x){    if(Tree[x].son[0])middle_order(Tree[x].son[0]);    for(int i=1;i<=Tree[x].num;i++)printf("%d ",Tree[x].key);    if(Tree[x].son[1])middle_order(Tree[x].son[1]);}int main(){//freopen("a.in","r",stdin);//freopen("a.out","w",stdout);scanf("%d",&n);int ans=0;for(int i=1;i<=n;i++){int val=0;scanf("%d",&val);Insert(val,root);if(i==1){ans+=val;continue;}int Pred = Find_pre(root), Succ = Find_succ(root);ans += min(fabs(Pred-val), fabs(Succ-val));}printf("%d\n",ans);return 0;}

或者使用c++的STL中的multiset来实现 这里发现如果先lower_bound来找答案的话会超时,而改为先插入当前元素,再使用find来计算答案就不会超时

/*happywu * 2014.8.12 * bzoj 1588 */#include<cstdio>#include<iostream>#include<set>#include<algorithm>using namespace std;multiset<int> q;multiset<int> ::iterator it;int n;int x;/*int main()//超时{freopen("a.in","r",stdin);scanf("%d",&n);multiset<long long >::iterator tmp;long long  ans=0;for(int i=1;i<=n;i++){scanf("%lld",&x);if(i==1){            ans=x;q.insert(x);            continue;}multiset<long long >::iterator it=lower_bound(q.begin(),q.end(),x);if(it==q.end()){it--;ans+=x-*it;it++;}else{if(it!=q.begin()){long long  k1=*it-x;it--;long long  k2=x-*it;ans+=min(k1,k2);}else{ans+=*it-x;}}q.insert(x);}printf("%lld\n",ans);return 0;}*/int main(){//freopen("a.in","r",stdin);scanf("%d",&n);int ans=0;for(int i=1;i<=n;i++){if(scanf("%d",&x)==EOF)x=0; //输入数据有问题q.insert(x);if(i==1) ans+=x;else{it=q.find(x);int b=1<<30;if(it!=q.begin()) it--,b=x-(*it),it++;it++;if(it!=q.end()) b=min(b,(*it)-x);ans+=b;}}printf("%d\n",ans);return 0;}

此题还可以用双向链表来实现 , 具体可见《基本数据结构在信息学竞赛中的应用》

#include<cstdio>#include<iostream>#include<algorithm>using namespace std;const int INF=1<<31-1;const int maxn=1000000+10;struct Node{int key,idx;bool operator<(const Node&b){if(key<b.key)return 1;return 0;}}a[maxn];int c[maxn],pre[maxn],nex[maxn];bool cmp(const Node&a,const Node&b){    if(a.key<b.key)return 1;    return 0;}inline void Erase(int i){nex[pre[c[i]]]=nex[c[i]];pre[nex[c[i]]]=pre[c[i]];}int main(){int n;scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&a[i].key);a[i].idx=i;}a[0].key=a[n+1].key=INF;a[0].idx=INF;a[n+1].idx=n+1;sort(a+1,a+n+1,cmp);for(int i=0;i<=n+1;i++)c[a[i].idx]=i; for(int i=1;i<=n;i++){nex[i]=i+1;pre[i]=i-1;}int ans=0;for(int i=n;i>=2;i--){ans+=min(abs(a[nex[c[i]]].key-a[c[i]].key),abs(a[pre[c[i]]].key-a[c[i]].key));Erase(i);}printf("%d\n",ans+a[c[1]].key);return 0;}

下面同样是双向链表,更加的简洁

#include<cstdio>#include<iostream>#include<algorithm>using namespace std;const int INF=1<<31-1;const int maxn=1000000+10;int a[maxn],id[maxn],pre[maxn],nex[maxn];bool cmp(int a,int b){return ::a[a]< ::a[b];  //必须要在前面加上 ::}inline void Erase(int x){nex[pre[x]]=nex[x];pre[nex[x]]=pre[x];}inline int read(){static int r,sign;static char c;r=0,sign=1;do {if((c=getchar())==EOF)return 0;}while(c!='-'&&(c<'0'||c>'9'));if(c=='-')sign=-1,c=getchar();while(c>='0' && c<='9') r=r*10+(int)(c-'0'),c=getchar();return sign*r;}int main(){int n;n=read();for(int i=1;i<=n;i++)a[i]=read();a[0]=a[n+1]=INF;for(int i=1;i<=n;i++)id[i]=i;sort(id+1,id+n+1,cmp);for(int i=1;i<=n;i++)nex[id[i]]=id[i+1],pre[id[i]]=id[i-1];int ans=0;for(int i=n;i>=2;i--){ans+=min(abs(a[nex[i]]-a[i]),abs(a[pre[i]]-a[i]));Erase(i);}printf("%d\n",ans+a[1]);return 0;}




0 0
原创粉丝点击