P2073 送花

P2073 送花

 

 

 

 因为c保证只会出现一次并且c小于等于106

我们可以以c为关键字,维护花费和优美度,构建一棵线段树

对于1操作,我们直接查询c节点是否有值,有就直接返回,反之赋值

对于2操作,删去最大值,就从完整区间开始,只要右子树有点或左子树无点,就尽可能的去遍历右儿子,否则才遍历左儿子,将最后到达的叶子节点清零

对于3操作,删去最小值,就从完整区间开始,只要左子树有点,就尽可能的去遍历左儿子,否则才遍历右儿子,将最后到达的叶子节点清零

最后输出整个区间的总优美度和总花费就行

代码:

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ls id<<1
#define rs id<<1|1
using namespace std;
const int maxn=1e6+10;
const int INF=0x7f7f7f7f;
int opt; 
bool vis[maxn]; 
struct Tree
{
    int l,r;
    int sumw,sumc;
    int minn,maxx;
}tree[maxn<<2];
void build(int id,int ll,int rr)
{
    tree[id].l=ll,tree[id].r=rr,tree[id].minn=INF; 
    if(ll==rr) return;
    int m=ll+rr>>1;
    build(ls,ll,m),build(rs,m+1,rr);
}
void update(int id,int pos,int val1,int val2,int val3,int val4)
{
    if(tree[id].l==pos && tree[id].r==pos) 
    {
        tree[id].sumc=val1;tree[id].sumw=val2;
        tree[id].minn=val3;tree[id].maxx=val4;
        return;
    }
    if(tree[ls].r>=pos) update(ls,pos,val1,val2,val3,val4);
    else update(rs,pos,val1,val2,val3,val4);
    tree[id].sumc=tree[ls].sumc+tree[rs].sumc;
    tree[id].sumw=tree[ls].sumw+tree[rs].sumw;
    tree[id].minn=min(tree[ls].minn,tree[rs].minn);
    tree[id].maxx=max(tree[ls].maxx,tree[rs].maxx);
}
int main()
{
    int n=maxn-10;
    build(1,1,n);
    while(scanf("%d",&opt)==1&&opt!=-1)
    {
        int w,c;
        if(opt==1) 
        {
            scanf("%d%d",&w,&c);
            if(vis[c])
                continue;
            update(1,c,c,w,c,c);
            vis[c]=true;
        }
        if(opt==3)
        {
            if(tree[1].minn==INF)
                continue;
            vis[tree[1].minn]=false,update(1,tree[1].minn,0,0,INF,0);
        }
        if(opt==2)
        {
            if(tree[1].maxx==0)
                continue;
            vis[tree[1].maxx]=false,update(1,tree[1].maxx,0,0,INF,0);
        }
    }
    printf("%d %d",tree[1].sumw,tree[1].sumc);
}

当然也可以运用平衡树的有关知识

将Splay运用的精确一些

就是fhqTreap

对于插入操作,假设权值为k。我们按照权值先split出小于等于k的一棵树x,再把split 出小于等于k1 的一棵树 y。如果 y 这棵子树不为空,也就是说已经有了权值为k 的节点,那么我们不插入即可。
对于删除操作,我们可以按照子树大小split 出我们要删除的值,然后不merge 即可。
最后 dfs 一遍求出答案即可。

贴一份有关这方面的代码(不是我写的)

#include<cstdio>
#include<cstdlib>
#define N 100005
#define int long long

int tot,Root;
int ans1,ans2;
int beauty[N];
int val[N],sze[N];
int ch[N][2],prio[N];

void pushup(int o){
  sze[o]=sze[ch[o][0]]+sze[ch[o][1]]+1;
}

void split(int o,int k,int &x,int &y){
  if(!o) x=y=0;
  else{
    if(sze[ch[o][0]]>=k){
      y=o;
      split(ch[o][0],k,x,ch[o][0]);
      pushup(o);
    }
    else{
      x=o;
      split(ch[o][1],k-sze[ch[o][0]]-1,ch[o][1],y);
      pushup(o);
    }
  }
}

int merge(int x,int y){
  if(!x or !y) return x+y;
  if(prio[x]<prio[y]){
    ch[x][1]=merge(ch[x][1],y);
    pushup(x);
    return x;
  }
  else{
    ch[y][0]=merge(x,ch[y][0]);
    pushup(y);
    return y;
  }
}

void split2(int o,int k,int &x,int &y){
  if(!o) x=y=0;
  else{
    if(val[o]<=k){
      x=o;
      split2(ch[o][1],k,ch[o][1],y);
    } else{
      y=o;
      split2(ch[o][0],k,x,ch[o][0]);
    }
    pushup(o);
  }
}

int newnode(int a,int b){
  sze[++tot]=1;
  prio[tot]=rand();
  beauty[tot]=a;
  val[tot]=b;
  return tot;
}

void insert(int o,int a,int b){
  int x,y,z;
  split2(Root,b,x,y);
  split2(x,b-1,x,z);
  if(sze[z]!=0) 
    Root=merge(x,merge(z,y));
  else
    Root=merge(x,merge(newnode(a,b),y));
}

void delex(){
  int siz=sze[Root];
  int a;
  split(Root,siz-1,Root,a);
}

void delch(){
  int a;
  split(Root,1,a,Root);
}

void dfs(int now){
  if(!now) return;
  ans1+=beauty[now];
  ans2+=val[now];
  dfs(ch[now][0]);
  dfs(ch[now][1]);
}

signed main(){
  int opt,a,b;
  while(scanf("%lld",&opt)){
    if(opt==-1) break;
    if(opt==1){
      scanf("%lld%lld",&a,&b);
      insert(Root,a,b);
    }
    if(opt==2)
      delex();
    if(opt==3)
      delch();
  }
  dfs(Root);
  printf("%lld %lld\n",ans1,ans2);
  return 0;
}

 

posted @ 2020-05-02 21:56  卍GC卐  阅读(138)  评论(0编辑  收藏  举报