SCOI2015 情报传递

题目链接:戳我

树链剖分把树上问题转换成序列上的问题qwq,然后求链上小于i-k的节点个数。

可以离线做,先把所有操作读入,这样就不需要中间的修改了qwq

然后就是大力主席树了qwqwq

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<ctime>
#include<cmath>
#define MAXN 400010

using namespace std;
int n,m,tt,tot,root,cnt;
int head[MAXN<<1],fa[MAXN],st[MAXN],rt[MAXN],sum[MAXN<<4],lc[MAXN<<4],rc[MAXN<<4];
int dep[MAXN],top[MAXN],siz[MAXN],son[MAXN],id[MAXN];
struct Node{int u,v,k,op;}node[MAXN];
struct Edge{int nxt,to;}edge[MAXN<<1];

inline void add(int from,int to){edge[++tt].nxt=head[from],edge[tt].to=to,head[from]=tt;}

inline void insert(int &x,int f,int l,int r,int pos)
{
    x=++tot;
    //printf("x=%d p=%d l=%d r=%d pos=%d\n",x,f,l,r,pos);
    lc[x]=lc[f],rc[x]=rc[f],sum[x]=sum[f]+1;
    if(l==r) return;
    int mid=(l+r)>>1;
    if(pos<=mid) insert(lc[x],lc[f],l,mid,pos);
    else insert(rc[x],rc[f],mid+1,r,pos);
}

inline int query(int l,int r,int ll,int rr,int k)
{
    if(l==r) return sum[rr]-sum[ll];
    int mid=(l+r)>>1;
    if(k<=mid) return query(l,mid,lc[ll],lc[rr],k);
    else return sum[lc[rr]]-sum[lc[ll]]+query(mid+1,r,rc[ll],rc[rr],k);
}

inline void build(int x)
{
    insert(rt[x],rt[fa[x]],1,m,st[x]);
    for(int i=head[x];i;i=edge[i].nxt)
    {
        int v=edge[i].to;
        build(v);
    }
}

inline void dfs1(int x)
{
    siz[x]=1;
    int maxx=-1;
    for(int i=head[x];i;i=edge[i].nxt)
    {
        int v=edge[i].to;
        dep[v]=dep[x]+1;
        dfs1(v);
        siz[x]+=siz[v];
        if(siz[v]>maxx) maxx=siz[v],son[x]=v;
    }
}

inline void dfs2(int x,int topf)
{
    top[x]=topf;
    id[x]=++cnt;
    if(son[x]) dfs2(son[x],topf);
    for(int i=head[x];i;i=edge[i].nxt)
    {
        int v=edge[i].to;
        if(v==son[x]) continue;
        dfs2(v,v);
    }
}

inline int LCA(int x,int y)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        x=fa[top[x]];
    }
    if(dep[x]<dep[y]) return x;
    else return y;
}

int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    freopen("ce.out","w",stdout);
    #endif
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&fa[i]);
        if(fa[i]==0) root=i; 
        add(fa[i],i);
    }
    scanf("%d",&m);
    for(int i=1;i<=n;i++) st[i]=m;
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&node[i].op);
        if(node[i].op==1) scanf("%d%d%d",&node[i].u,&node[i].v,&node[i].k);
        else scanf("%d",&node[i].u),st[node[i].u]=i;
    }
    dep[root]=1;
    dfs1(root);
    dfs2(root,root);
    build(root);
    //for(int i=1;i<=n;i++) printf("rt[%d]=%d\n",i,rt[i]); puts("");
    //for(int i=1;i<=n;i++) printf("dep[%d]=%d\n",i,dep[i]); puts("");
    //for(int i=1;i<=n;i++) printf("fa[%d]=%d\n",i,fa[i]);
    for(int i=1;i<=m;i++)
    {
        int op=node[i].op,u,v,k;
        if(op==1)
        {
            u=node[i].u,v=node[i].v,k=node[i].k;
            int lca=LCA(u,v);
            int cur_ans=0;
            if(i-k<=1) 
            {
                printf("%d 0\n",dep[u]+dep[v]-2*dep[lca]+1);
                continue;
            }
            cur_ans+=query(1,m,rt[lca],rt[u],i-k-1);
            cur_ans+=query(1,m,rt[lca],rt[v],i-k-1);
            cur_ans+=(st[lca]<(i-k));
            printf("%d %d\n",dep[u]+dep[v]-2*dep[lca]+1,cur_ans);
        }
    }
    return 0;
}
posted @ 2019-02-27 17:36  风浔凌  阅读(152)  评论(0编辑  收藏  举报