UOJ Logo zhouzixuan的博客

博客

bzoj 4336: BJOI2015 骑士的旅行

2015-11-22 15:57:09 By zhouzixuan
【题目描述】:
    在一片古老的土地上,有一个繁荣的文明。
    这片大地几乎被森林覆盖,有N座城坐落其中。巧合的是,这N座城由恰好N-1条双向道路连接起来,使得任意两座城都是连通的。也就是说,这些城形成了树的结构,任意两座城之间有且仅有一条简单路径。
    在这个文明中,骑士是尤其受到尊崇的职业。任何一名骑士,都是其家族乃至家乡的荣耀。Henry从小就渴望成为一名能守护家乡、驱逐敌人的骑士。勤奋训练许多年后,Henry终于满18岁了。他决定离开家乡,向那些成名已久的骑士们发起挑战!
    根据Henry的调查,大陆上一共有M名受封骑士,不妨编号为1到M。
    第i个骑士居住在城Pi,武力值为Fi。
    Henry计划进行若干次旅行,每次从某座城出发沿着唯一的简单路径前往另一座城,同时会挑战路线上武力值最高的K个骑士(Henry的体力有限,为了提高水平,当然要挑战最强的骑士)。如果路线上的骑士不足K人,Henry会挑战遇到的所有人。
    每次旅行前,可能会有某些骑士的武力值或定居地发生变化,Henry自然会打听消息,并对计划做出调整。
    为了在每次旅行时做好充分准备,Henry希望你能帮忙在每次旅行前计算出这条路线上他将挑战哪些对手。
【题目解法】:
    考虑没有修改的情况,我们用T[x]表示root->x的主席树
    然后我们查询的时候只需要在T[x]+T[y]-T[lca(x,y))+T[fa[lca(x,y)]]这颗主席树上二分即可
    现在我们有了修改操作:考虑每次修改操作,影响到的点为子树中的点
    因此我们可以弄出dfs序,那么修改就变成了修改某一段连续点的主席树
    我们可以外层树状数组,内层线段树维护即可
    复杂度O(N log N log 1000)
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=80005;
const int M=30;
struct edge {int x,next;} b[N*2];
struct node {int lc,rc,t;} tree[N*50];
struct KnightNode {int p,x;} knight[N];
int n,m,q,rank,dfn[N],L[N],R[N],cnt,fa[21][N],dep[N];
int A[M],B[M],C[M],D[M],fight[N],ans,tot,a[N],root[N],Ans[N];
inline long long getint()
{
    long long x=0; char c=getchar(); bool flag=false;
    while ((c!='-')&&((c<'0')||(c>'9'))) c=getchar();
    if (c=='-') flag=true,c=getchar();
    while ((c>='0')&&(c<='9')) x=x*10+(long long)(c-'0'),c=getchar();
    if (flag) return -x; else return x;
}
inline int lowbit(int x)
{
    return x&(-x);
}
inline void addedge(int x,int y)
{
    ++tot;
    b[tot].x=y;
    b[tot].next=a[x];
    a[x]=tot;
}
void init()
{
    n=getint(); cnt=0;
    for (int i=1; i<n; i++)
    {
        int x=getint(),y=getint();
        addedge(x,y); addedge(y,x);
    }
    tot=0;
}
void dfs(int x,int pre,int depth)
{
    dfn[x]=++cnt; L[x]=cnt; fa[0][x]=pre; dep[x]=depth;
    for (int p=a[x];p;p=b[p].next) if (b[p].x!=pre) dfs(b[p].x,x,depth+1);
    R[x]=cnt;
}
void modify(int &p,int l,int r,int x,int y)
{
    if (p==0) p=++tot; tree[p].t+=y;
    int mid=(l+r)/2; if (l==r) return;
    if (x<=mid) modify(tree[p].lc,l,mid,x,y);
    else modify(tree[p].rc,mid+1,r,x,y);
}
void modify(int x,int y,int flag)
{
    while (x>0) 
    {
        modify(root[x],1,1000,y,flag); 
        x-=lowbit(x);
    }
}
void modify(int l,int r,int x,int flag)
{
    modify(r,x,flag);
    modify(l-1,x,-flag);
}
void ask(int x,int now[])
{
    now[0]=0; if (x==0) return;
    while (x<=n) now[++now[0]]=root[x],x+=lowbit(x);
}
inline int LCA(int x,int y)
{
    if (dep[x]<dep[y]) swap(x,y);
    for (int j=0,k=dep[x]-dep[y];k;k/=2,j++) if (k%2==1) x=fa[j][x];
    if (x==y) return x;
    for (int i=20; i>=0; i--) if (fa[i][x]!=fa[i][y]) x=fa[i][x],y=fa[i][y];
    return fa[0][x];
}
void print(int a[],int b[],int c[],int d[],int l,int r)
{
    int A[M],B[M],C[M],D[M];
    int nowans=0;
    for (int i=1; i<=a[0]; i++) nowans+=tree[a[i]].t;
    for (int i=1; i<=b[0]; i++) nowans+=tree[b[i]].t;
    for (int i=1; i<=c[0]; i++) nowans-=tree[c[i]].t;
    for (int i=1; i<=d[0]; i++) nowans-=tree[d[i]].t;
    if (l==r) {for (int i=1; i<=nowans; i++) Ans[++cnt]=l; return;}
    if (nowans==0) return; int mid=(l+r)/2;
    for (int i=1; i<=a[0]; i++) A[i]=tree[a[i]].rc; A[0]=a[0];
    for (int i=1; i<=b[0]; i++) B[i]=tree[b[i]].rc; B[0]=b[0];
    for (int i=1; i<=c[0]; i++) C[i]=tree[c[i]].rc; C[0]=c[0];
    for (int i=1; i<=d[0]; i++) D[i]=tree[d[i]].rc; D[0]=d[0];
    print(A,B,C,D,mid+1,r);
    for (int i=1; i<=a[0]; i++) A[i]=tree[a[i]].lc; A[0]=a[0];
    for (int i=1; i<=b[0]; i++) B[i]=tree[b[i]].lc; B[0]=b[0];
    for (int i=1; i<=c[0]; i++) C[i]=tree[c[i]].lc; C[0]=c[0];
    for (int i=1; i<=d[0]; i++) D[i]=tree[d[i]].lc; D[0]=d[0];
    print(A,B,C,D,l,mid);
}
void ask(int l,int r,int k)
{
    int nowans=0; int mid=(l+r)/2;
    for (int i=1; i<=A[0]; i++) nowans+=tree[tree[A[i]].rc].t;
    for (int i=1; i<=B[0]; i++) nowans+=tree[tree[B[i]].rc].t;
    for (int i=1; i<=C[0]; i++) nowans-=tree[tree[C[i]].rc].t;
    for (int i=1; i<=D[0]; i++) nowans-=tree[tree[D[i]].rc].t;
    if (l==r) 
    {
        nowans=0;
        for (int i=1; i<=A[0]; i++) nowans+=tree[A[i]].t;
        for (int i=1; i<=B[0]; i++) nowans+=tree[B[i]].t;
        for (int i=1; i<=C[0]; i++) nowans-=tree[C[i]].t;
        for (int i=1; i<=D[0]; i++) nowans-=tree[D[i]].t;
        for (int i=1; i<=min(k,nowans); i++) Ans[++cnt]=l; return;
    }
    if (k<=nowans)
    {
        for (int i=1; i<=A[0]; i++) A[i]=tree[A[i]].rc;
        for (int i=1; i<=B[0]; i++) B[i]=tree[B[i]].rc;
        for (int i=1; i<=C[0]; i++) C[i]=tree[C[i]].rc;
        for (int i=1; i<=D[0]; i++) D[i]=tree[D[i]].rc;
        ask(mid+1,r,k);
    }
    else
    {
        int a[M],b[M],c[M],d[M];
        for (int i=1; i<=A[0]; i++) a[i]=tree[A[i]].rc; a[0]=A[0];
        for (int i=1; i<=B[0]; i++) b[i]=tree[B[i]].rc; b[0]=B[0];
        for (int i=1; i<=C[0]; i++) c[i]=tree[C[i]].rc; c[0]=C[0];
        for (int i=1; i<=D[0]; i++) d[i]=tree[D[i]].rc; d[0]=D[0];
        print(a,b,c,d,mid+1,r);
        for (int i=1; i<=A[0]; i++) A[i]=tree[A[i]].lc;
        for (int i=1; i<=B[0]; i++) B[i]=tree[B[i]].lc;
        for (int i=1; i<=C[0]; i++) C[i]=tree[C[i]].lc;
        for (int i=1; i<=D[0]; i++) D[i]=tree[D[i]].lc;
        ask(l,mid,k-nowans);
    }
}
void query(int x,int y,int k)
{
    int K=LCA(x,y);
    ask(dfn[x],A); ask(dfn[y],B); ask(dfn[K],C); ask(dfn[fa[0][K]],D);
    cnt=0; ask(1,1000,k); if (cnt==0) {printf("%d\n",-1); return;}
    for (int i=1; i<=cnt; i++) printf("%d ",Ans[i]); printf("\n");
}
void prepare()
{
    for (int i=1; i<=20; i++)
        for (int j=1; j<=n; j++)
        fa[i][j]=fa[i-1][fa[i-1][j]];
    m=getint();
    for (int i=1; i<=m; i++)
    {
        knight[i].x=getint(); knight[i].p=getint(); fight[knight[i].x]++;
        modify(L[knight[i].p],R[knight[i].p],knight[i].x,1);
    }
}
void solve()
{
    q=getint(); rank=getint();
    for (int i=1; i<=q; i++)
    {
        int opt=getint(),x=getint(),y=getint();
        if (opt==1) query(x,y,rank);
        if (opt==2)
        {
            modify(L[knight[x].p],R[knight[x].p],knight[x].x,-1);
            knight[x].p=y;
            modify(L[knight[x].p],R[knight[x].p],knight[x].x,1);
        }
        if (opt==3)
        {
            modify(L[knight[x].p],R[knight[x].p],knight[x].x,-1);
            knight[x].x=y; fight[knight[x].x]--; fight[y]++;
            modify(L[knight[x].p],R[knight[x].p],knight[x].x,1);
        }
    }
}
int main()
{
    init();
    dfs(1,0,1);
    prepare();
    solve();
    return 0;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。