1. 程式人生 > >bzoj4919 大根堆(線段樹合併)

bzoj4919 大根堆(線段樹合併)

也是雅禮集訓考的一題。。。

似乎不必要用線段樹合併,用個set也能又好又快的水過去。。。

先考慮最簡單的樹形dp怎麼做;

設dp[i][j]表示當前在i節點並且選的最大不超過j的答案是多少;

考慮用動態開點的線段樹來維護dp陣列

對於每一個節點,只需要先把其兒子的dp陣列加起來,這就對應線段樹合併操作;又因為我用的標記永久化,每一次修改對應區間直接相加;

再根據自己本身的值進行略微修改就好;

先考慮每一次會怎麼修改,很顯然的是對於每一個dp陣列都是單調不降的。而又要從最大值進行轉移;
設當前點的值為v,那麼選這個點的貢獻則為dp[i][v-1]+1;

再考慮新新增的值怎麼加進線段樹中,那麼就要把v到maxv中所有比新貢獻小的進行修改;
而每次修改最多隻會加1(因為單調性嘛。。。

所以可以二分出需要修改的點,每一次二分都要一個log從線段樹裡查值,再加上線段樹合併的複雜度,總體還是n(logn)^2的

具體細節看程式碼

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define LL long long
const int maxn=200005;
struct NODE{
    int col,l,r;
}t[maxn*40];
int ntot=0;
int rt[maxn],nv[maxn],v[maxn],vtot=0,node[maxn];
void
modify(int l,int r,int &x,int nl,int nr,int v) { if(!x)x=++ntot; if(nl<=l&&nr>=r){ t[x].col+=v; return; } int mid=(l+r)>>1; if(nl<=mid)modify(l,mid,t[x].l,nl,nr,v); if(nr>mid)modify(mid+1,r,t[x].r,nl,nr,v); } int query(int l,int
r,int x,int pos){ if(!pos)return 0; if(!x)return 0; int tans=t[x].col; int mid=(l+r)>>1; if(pos<=mid)tans+=query(l,mid,t[x].l,pos); else tans+=query(mid+1,r,t[x].r,pos); return tans; } int merge(int x,int y){ if(!x||!y)return x+y; t[x].col+=t[y].col; t[x].l=merge(t[x].l,t[y].l); t[x].r=merge(t[x].r,t[y].r); return x; } struct EDGE{ int next,to; }edge[maxn]; int etot=0; void add(int x,int y){ edge[++etot].next=node[x]; edge[etot].to=y; node[x]=etot; } void dfs(int x){ rt[x]=0; for(int i=node[x];i;i=edge[i].next){ dfs(edge[i].to); rt[x]=merge(rt[edge[i].to],rt[x]); } int mv=query(1,vtot,rt[x],nv[x]-1)+1; if(mv<=query(1,vtot,rt[x],nv[x]))return; int l=nv[x],r=vtot; while(l<r){ int mid=((l+r)>>1)+((l+r)&1); if(query(1,vtot,rt[x],mid)<mv)l=mid; else r=mid-1; } modify(1,vtot,rt[x],nv[x],l,1); } int n=0; int main() { scanf("%d",&n); for(int i=1;i<=n;i++){ int tf; scanf("%d%d",&nv[i],&tf); add(tf,i); v[++vtot]=nv[i]; } std::sort(v+1,v+vtot+1); vtot=std::unique(v+1,v+vtot+1)-v-1; for(int i=1;i<=n;i++)nv[i]=std::lower_bound(v+1,v+vtot+1,nv[i])-v; dfs(1); printf("%d\n",query(1,vtot,rt[1],vtot)); return 0; }

恩。。因為我的二分查值是實實在在的log^2,成功跑到bzoj rank 倒1;