JZOJ 5850. 【NOIP提高组模拟2018.8.25】e

38 篇文章 0 订阅
6 篇文章 0 订阅

Description

Description

Input

Input

Output

Output

Sample Input

5 7 0
1 2 3 4 5
1 2
2 3
2 4
1 5
1 2 4 5
2 2 4 5
3 2 4 5
4 2 4 5
5 2 4 5
5 1 2
100 3 1 2 5

Sample Output

0
0
1
0
0
3
95

Data Constraint

Data Constraint

Solution

  • 挺不错的一题!

  • 首先对于一个 k k 个点构成的连通块,就是那 k 个点的lca,之后延伸出来许多条链。

  • 那么我们先找到那个lca,再对于每一条链都找出最接近 r r 的值。

  • 这怎么办呢?建可持久化的权值线段树(主席树)就可以了,查询 比它大最小的 和 比它小的最大的 两个值。

  • 我的方法是树链剖分后建树,在每条重链中查询,但还有更好的方法是每个点从它的父亲处继承。

  • 时间复杂度 O(klog2n)

Code

#include<cstdio>
#include<cctype>
using namespace std;
const int N=1e5+5,inf=1e9;
struct data
{
    int v,l,r;
}f[N*32];
int tot,last,qx,qy,pos;
int first[N],nex[N<<1],en[N<<1];
int fa[N],dep[N],son[N],size[N];
int top[N],pre[N],tree[N];
int a[N],p[N],rt[N];
inline int read()
{
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
void write(int x)
{
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
inline void insert(int x,int y)
{
    nex[++tot]=first[x];
    first[x]=tot;
    en[tot]=y;
}
void dfs(int x)
{
    dep[x]=dep[fa[x]]+1;
    size[x]=1;
    for(int i=first[x];i;i=nex[i])
        if(en[i]^fa[x])
        {
            fa[en[i]]=x;
            dfs(en[i]);
            size[x]+=size[en[i]];
            if(!son[x] || son[en[i]]>size[son[x]]) son[x]=en[i];
        }
}
void dfs1(int x,int y)
{
    top[pre[tree[x]=++tot]=x]=y;
    if(!son[x]) return;
    dfs1(son[x],y);
    for(int i=first[x];i;i=nex[i])
        if(en[i]^fa[x] && en[i]^son[x]) dfs1(en[i],en[i]);
}
int lca(int x,int y)
{
    while(top[x]^top[y])
        if(dep[top[x]]>dep[top[y]]) x=fa[top[x]]; else y=fa[top[y]];
    return dep[x]<dep[y]?x:y;
}
inline int abs(int x)
{
    return x<0?-x:x;
}
inline int min(int x,int y)
{
    return x<y?x:y;
}
void change(int ff,int &v,int l,int r)
{
    f[v=++tot]=f[ff];
    f[v].v++;
    if(l==r) return;
    int mid=l+r>>1;
    if(pos<=mid) change(f[ff].l,f[v].l,l,mid); else change(f[ff].r,f[v].r,mid+1,r);
}
int down(int ff,int v,int l,int r)
{
    if(!(f[v].v-f[ff].v)) return 0;
    if(l==r) return l;
    int mid=l+r>>1;
    if(qx<=mid) return down(f[ff].l,f[v].l,l,mid);
    int s=down(f[ff].r,f[v].r,mid+1,r);
    return s?s:down(f[ff].l,f[v].l,l,mid);
}
int up(int ff,int v,int l,int r)
{
    if(!(f[v].v-f[ff].v)) return 0;
    if(l==r) return l;
    int mid=l+r>>1;
    if(qx>mid) return up(f[ff].r,f[v].r,mid+1,r);
    int s=up(f[ff].l,f[v].l,l,mid);
    return s?s:up(f[ff].r,f[v].r,mid+1,r);
}
int main()
{
    freopen("e.in","r",stdin);
    freopen("e.out","w",stdout);
    int n=read(),q=read(),ty=read();
    if(!q) return 0;
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        insert(x,y);
        insert(y,x);
    }
    tot=0;
    dfs(1);
    dfs1(1,1);
    tot=0;
    for(int i=1;i<=n;i++) pos=a[pre[i]],change(rt[i-1],rt[i],1,inf);
    while(q--)
    {
        int r=read(),k=read();
        for(int i=1;i<=k;i++) p[i]=(read()-1+last*ty)%n+1;
        pos=p[1];
        for(int i=2;i<=k && pos>1;i++) pos=lca(pos,p[i]);
        last=abs(a[pos]-r);
        qx=r;
        for(int i=1;i<=k && last;i++)
        {
            int x=p[i];
            while(top[x]^top[pos])
            {
                int num=down(rt[tree[top[x]]-1],rt[tree[x]],1,inf);
                if(num) last=min(last,abs(num-r));
                num=up(rt[tree[top[x]]-1],rt[tree[x]],1,inf);
                if(num) last=min(last,abs(num-r));
                x=fa[top[x]];
            }
            int num=down(rt[tree[pos]-1],rt[tree[x]],1,inf);
            if(num) last=min(last,abs(num-r));
            num=up(rt[tree[pos]-1],rt[tree[x]],1,inf);
            if(num) last=min(last,abs(num-r));
        }
        write(last),putchar('\n');
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值