HDU5420:Victor and Proposition(KD树)

10 篇文章 0 订阅
6 篇文章 0 订阅

传送门

题解:
这是经典的KD树模型,所以直接KD树优化建边即可。

不过需要注意的是KD树上的点不能是原来的点,要新建一个映射点连向原来的点,因为原来的点如果增加了本来没有的连边会对答案有影响。。

(刚才搜了搜好像全是线段树?? 赶紧去学一学)

upt::
线段树做法,可持久化线段树合并,比kd树好打多了。。
Code : https://paste.ubuntu.com/26511273/

#include <bits/stdc++.h>
using namespace std;
inline int rd() {
    char ch=getchar(); int i=0,f=1;
    while(!isdigit(ch)) {if(ch=='-')f=-1; ch=getchar();}
    while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=getchar();}
    return i*f;
}
const int N=2e5+50;
int n,tot,dfn[N],dep[N],sze[N],id[N],ind;
int scc,xc[N],d[N],low[N],st[N],ins[N],tp;
long long ans;
struct point {
    int x,y;
    point(){}
    point(int x,int y):x(x),y(y){}
}que[N];
inline bool cmpx(const point &a,const point &b) {return a.x<b.x;}
inline bool cmpy(const point &a,const point &b) {return a.y<b.y;}
struct node {
    node *lc,*rc;
    point p;
    int o;
    int mx[2],mn[2];
    inline void upt(node *x,node *y) {
        y->mx[0]=max(y->mx[0],x->mx[0]);
        y->mx[1]=max(y->mx[1],x->mx[1]);
        y->mn[0]=min(y->mn[0],x->mn[0]);
        y->mn[1]=min(y->mn[1],x->mn[1]);
    }
    inline void upt() {
        mx[0]=(mn[0]=p.x);
        mx[1]=(mn[1]=p.y);
        if(lc)upt(lc,this);
        if(rc)upt(rc,this);
    }
}nd[N],*rt=nd+1,*pool=rt;
struct Graph*G;
struct Graph{
    vector <int> edge[N];
    inline void init(int sze) {
        for(int i=1;i<=sze;i++) edge[i].clear();
    }
    inline void add(int x,int y) {
        if(x==y) return;
        edge[x].push_back(y);
    }
    inline void dfs(int x) {
        dfn[x]=++ind; id[ind]=x; sze[x]=1;
        for(int e=edge[x].size()-1;e>=0;e--) {
            int v=edge[x][e]; 
            dep[v]=dep[x]+1;
            dfs(v); sze[x]+=sze[v];
        }
        que[x]=point(dfn[x],dep[x]);
    }
    inline void build(node *&now,int l,int r,int dim) {
        now=++pool;
        now->o=++tot;
        if(l==r) {
            now->p=que[l];
            G->add(tot,id[now->p.x]);
            now->upt();
            return;
        } int mid=(l+r)>>1;
        if(dim) nth_element(que+l,que+mid,que+r+1,cmpy);
        else nth_element(que+l,que+mid,que+r+1,cmpx);
        now->p=que[mid];
        G->add(tot,id[now->p.x]);
        if(l<mid) build(now->lc,l,mid-1,dim^1),G->add(now->o,now->lc->o);
        if(r>mid) build(now->rc,mid+1,r,dim^1),G->add(now->o,now->rc->o);
        now->upt();
    }
    inline int cmp(const point &mn,const point &mx,node *now) {
        if(now->mx[0]<mn.x||now->mx[1]<mn.y) return -1;
        if(now->mn[0]>mx.x||now->mn[1]>mx.y) return -1;
        if(now->mn[0]>=mn.x&&now->mn[1]>=mn.y&&now->mx[0]<=mx.x&&now->mx[1]<=mx.y) return 1;
        return 0;
    }
    inline bool in(const point &mn,const point &mx,const point &p) {
        return p.x>=mn.x&&p.x<=mx.x&&p.y>=mn.y&&p.y<=mx.y;
    }
    inline void findarea(node *now,int i,const point &mn,const point &mx) {
        if(!now) return;
        int t=cmp(mn,mx,now);
        if(t==-1) return;
        if(t==1) G->add(i,now->o);
        else {
            findarea(now->lc,i,mn,mx);
            findarea(now->rc,i,mn,mx);
            if(in(mn,mx,now->p)) G->add(i,id[now->p.x]);
        }
    }
    inline void build() {
        ind=0; dfs(1);
        G->init(2*n);
        pool=rt;
        for(int i=1;i<=n;i++) nd[i].lc=NULL,nd[i].rc=NULL;
        build(rt,1,n,0);
        for(int i=1;i<=n;i++) {
            findarea(rt,i,point(dfn[xc[i]],dep[xc[i]]),point(dfn[xc[i]]+sze[xc[i]]-1,dep[xc[i]]+d[i]));
        }
    }
    inline void tarjan(int x) {
        dfn[x]=(low[x]=++ind); ins[x]=1; st[++tp]=x;
        for(int e=edge[x].size()-1;e>=0;e--) {
            int v=edge[x][e];
            if(!dfn[v]) tarjan(v),low[x]=min(low[x],low[v]);
            else if(ins[v]) low[x]=min(low[x],dfn[v]);
        }
        if(low[x]==dfn[x]) {
            ++scc; sze[scc]=0;
            int u;
            do{
                u=st[tp--];
                ins[u]=0;
                if(u<=n) ++sze[scc];
            }while(u!=x);
            ans+=((long long)sze[scc]*(sze[scc]-1)/2);
        }
    }
    inline void tarjan() {
        ind=0; scc=0;
        memset(dfn+1,0,sizeof(int)*tot);
        for(int i=1;i<=tot;i++) if(!dfn[i])
            tarjan(i);
    }
}tr,gh;
inline void solve() {
    n=rd(); tot=n;
    tr.init(n);
    for(int i=2;i<=n;i++) tr.add(rd(),i);
    for(int i=1;i<=n;i++) xc[i]=rd(),d[i]=rd();
    tr.build();
    ans=0;
    gh.tarjan();
    printf("%lld\n",ans);
}
int main() {
    G=&gh;
    int T=rd();
    while(T--) solve();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值