可并堆

左偏树

bzoj4003 城池攻占

题目大意:一棵树,每个点有一个防御值。m个武士,有攻击力和起始的位置,攻下一个点后会向父亲进攻,攻击力大于等于一个点的防御力就可以攻下,否则死亡。武士攻下每个点后攻击力会变化,加上或者乘上一个数(乘的数保证非负)。问每个城市死亡的武士个数和每个武士攻下的点。

思路:用左偏树维护一个点的武士信息,权值的更改用*a+b的形式更新。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 300005
#define LL long long
using namespace std;
struct use{int l,r,d;LL ad,ch;}hp[N];
struct uu{int ai;LL vi,hi;}ci[N];
int rt[N]={0},point[N]={0},next[N],cur[N],zh[N],zt,pa[N],ca[N]={0},dis[N]={0};
LL vi[N];
void pushdown(int x){
    int l,r;l=hp[x].l;r=hp[x].r;
    if (l){
        vi[l]=vi[l]*hp[x].ch+hp[x].ad;
        hp[l].ad=hp[l].ad*hp[x].ch+hp[x].ad;
        hp[l].ch=hp[l].ch*hp[x].ch;
    }if (r){
        vi[r]=vi[r]*hp[x].ch+hp[x].ad;
        hp[r].ad=hp[r].ad*hp[x].ch+hp[x].ad;
        hp[r].ch=hp[r].ch*hp[x].ch;
    }hp[x].ad=0LL;hp[x].ch=1LL;
}
int merge(int x,int y){
    if (!x) return y;
    if (!y) return x;
    pushdown(x);pushdown(y);
    if (vi[x]>vi[y]) swap(x,y);
    hp[x].r=merge(hp[x].r,y);
    if (hp[hp[x].l].d<hp[hp[x].r].d) swap(hp[x].l,hp[x].r);
    if (!hp[x].r) hp[x].d=0;
    else hp[x].d=hp[hp[x].r].d+1;
    return x;}
void work(int u){
    while(rt[u]&&vi[rt[u]]<ci[u].hi){
        ++ca[u];pushdown(rt[u]);
        pa[rt[u]]=dis[pa[rt[u]]]-dis[u];
        rt[u]=merge(hp[rt[u]].l,hp[rt[u]].r);
    }if (u!=1){
        if (rt[u]){
            if (ci[u].ai==0){
                vi[rt[u]]+=ci[u].vi;
                hp[rt[u]].ad+=ci[u].vi;
            }else{
                vi[rt[u]]*=ci[u].vi;
                hp[rt[u]].ad*=ci[u].vi;
                hp[rt[u]].ch*=ci[u].vi;
            }
        }
    }else
        while(rt[u]){
            pushdown(rt[u]);
            pa[rt[u]]=dis[pa[rt[u]]]-dis[u]+1;
            rt[u]=merge(hp[rt[u]].l,hp[rt[u]].r);
        }
}
int main(){
    int n,m,i,fi,u,v;
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;++i) scanf("%I64d",&ci[i].hi);
    for (i=2;i<=n;++i){
        scanf("%d%d%I64d",&fi,&ci[i].ai,&ci[i].vi);
        next[i]=point[fi];cur[fi]=point[fi]=i;
    }for (i=1;i<=m;++i){
        scanf("%I64d%d",&vi[i],&fi);
        pa[i]=fi;
        hp[i]=(use){0,0,0,0LL,1LL};
        rt[fi]=merge(rt[fi],i);
    }zh[zt=1]=1;dis[1]=1;
    while(zt){
        u=zh[zt];
        if ((v=cur[u])>0){
            dis[v]=dis[u]+1;
            zh[++zt]=v;
            cur[u]=next[v];
        }else{
            --zt;
            for (v=point[u];v;v=next[v])
                rt[u]=merge(rt[u],rt[v]);
            work(u);
        }
    }for (i=1;i<=n;++i) printf("%d\n",ca[i]);
    for (i=1;i<=m;++i) printf("%d\n",pa[i]);
}
View Code

 

bzoj4524 伪光滑数

题目大意:x的最大质因子ak,分解后有k项(9=3*3,算两项),ak^k<=N,问第e大的数x是多少。

思路:优先队列保存最大的质因数是x,有y项的最大数(!!)。fi[i][j]表示最大质因数是i,有j项的数的堆,gi[i][j]表示最大质因数至多到i,有j项的数的堆。找次大数的时候可以用可持久化左偏堆维护,因为会不断建点,所以防止mle。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#define N 128
#define up 61
#define len 16000000
#define LL long long
using namespace std;
struct use{
    int x,y;LL v;
    bool operator<(const use&x)const{return v<x.v;}
};
struct hp{int l,r,d;LL v,del;}zh[len];
int fi[N][up]={0},gi[N][up]={0},pr[N],flag[N]={0},zt=0;
LL vi[N][up],n;
priority_queue<use> que;
int getn(int o){
    if (!o) return 0;
    zh[++zt]=zh[o];return zt;}
int add(int o,LL x){
    if (!o) return o;
    int ci=getn(o);
    zh[ci].del*=x;zh[ci].v*=x;
    return ci;}
void pushdown(int o){
    if (zh[o].del==1LL) return;
    zh[o].l=add(zh[o].l,zh[o].del);
    zh[o].r=add(zh[o].r,zh[o].del);
    zh[o].del=1LL;}
int merge(int a,int b){
    if (!a) return b;
    if (!b) return a;
    pushdown(a);pushdown(b);
    if (zh[a].v<zh[b].v) swap(a,b);
    int c1=getn(a);
    zh[c1].r=merge(zh[c1].r,b);
    if ((!zh[c1].l?0:zh[zh[c1].l].d)<(!zh[c1].r?0:zh[zh[c1].r].d))
        swap(zh[c1].l,zh[c1].r);
    if (!zh[c1].r) zh[c1].d=1;
    else zh[c1].d=zh[zh[c1].r].d+1;
    return c1;}
void del(int &o){
    if (!o) return;
    pushdown(o);
    o=merge(zh[o].l,zh[o].r);}
void pre(int nn){
    int i,j,k,ci;
    for (i=2;i<nn;++i){
        if (!flag[i]) pr[++pr[0]]=i;
        for (j=1;j<=pr[0]&&i*pr[j]<nn;++j){
            flag[i*pr[j]]=true;
            if (i%pr[j]==0) break;
        }
    }memset(vi,0,sizeof(vi));
    for (i=1;i<=pr[0];++i){
        vi[i][0]=1LL;
        for (vi[i][j=1]=(LL)pr[i];vi[i][j]<=n&&vi[i][j]>vi[i][j-1];){
            ++j;vi[i][j]=vi[i][j-1]*(LL)pr[i];
        }vi[i][j]=0LL;
    }gi[0][0]=zt=1;
    zh[1]=(hp){0,0,0,1LL,1LL};
    for (i=1;i<=pr[0];++i){
        gi[i][0]=1;
        for (j=1;vi[i][j];++j){
            for (k=1;k<=j;++k)
                if (gi[i-1][j-k]){
                    ci=add(gi[i-1][j-k],vi[i][k]);
                    fi[i][j]=merge(fi[i][j],ci);
                }
            gi[i][j]=merge(fi[i][j],gi[i-1][j]);
            que.push((use){i,j,zh[fi[i][j]].v});
        }
    }
}
int main(){
    LL ans;use ci;
    int k,i,j,mx;scanf("%I64d%d",&n,&k);
    pre(N);
    while(k--){
        ci=que.top();que.pop();
        ans=ci.v;
        del(fi[ci.x][ci.y]);
        if (fi[ci.x][ci.y]){
            ci.v=zh[fi[ci.x][ci.y]].v;
            que.push(ci);
        }
    }printf("%I64d\n",ans);
}
View Code

(一开始想的保存所有质因数是第几个,维护一个递增序列,用treap维护,但复杂度过不去)

posted @ 2016-03-31 21:11  Rivendell  阅读(606)  评论(0编辑  收藏  举报