2023NOIP A层联测26-tour

大炎是一个历史悠久的国家,上古时代,岁兽统治着大炎,各个城市之间都没有道路往来。岁兽被击溃,陷入沉睡后,各个城市之间开始修建道路,积极进行文化交流。

年对这段历史十分感兴趣,总盘算着借这个题材拍摄她的特效大电影,但大炎的历史过于纷繁复杂,凭她的脑阔是无法完美复现的。于是她找到了你——刚学习了 C++ ,拿到泰拉大陆 IOI 金牌的博士,来帮助她复现当时的情景。

首先,她会告诉你大炎的城市数 n n n 与每个城市的文化值 v a l i val_i vali

然后接下来她会给你 q q q 次操作,操作有两种:

0 x y 代表城市 x x x 与城市 y y y 之间修建起了一条道路(保证修建前 x x x y y y 不连通)

1 x y 代表有一个人,初始时他的文化值为 0 0 0,他会从 x x x 走到 y y y(保证此时 x x x y y y 连通),每走到一个城市(设其为 i i i),他会与这个城市进行文化交流,如果此时他的文化值大于等于 v a l i val_i vali,那么这次文化交流是成功的。无论文化交流结果如何,在此之后,他的文化值会加上 v a l i val_i vali。年要让你输出,成功的文化交流的次数。

大部分时候,年还是很有耐心的,会先给出所有操作再听你慢慢回答,但是有时她刚吃了火锅那就会变得急躁,要求你立刻回答她的询问,因此本题部分测试点强制在线

n ≤ 1 0 5 , q ≤ 5 × 1 0 5 n\le10^5,q\le5\times10^5 n105,q5×105


先考虑离线的情况,显然可以先把树建好再依次回答询问。

d i s x dis_x disx 表示根到 x x x 之间的点权和。

对于查询 x , y x,y x,y,可以分成 x x x l c a lca lca l c a lca lca y y y 两种情况。

对于在 x x x l c a lca lca 路径上的 i i i i i i 要满足条件当且仅当 d i s x − d i s i ≥ d i s i − d i s f a i dis_x-dis_i\ge dis_i-dis_{fa_i} disxdisidisidisfai,移项得 2 d i s i − d i s f a i ≤ d i s x 2dis_i-dis_{fa_i}\le dis_x 2disidisfaidisx

对于在 y y y l c a lca lca 路径上的 i i i i i i 要满足条件当且仅当 d i s x − d i s f a l c a ( x , y ) + d i s f a i − d i s l c a ( x , y ) ≥ d i s i − d i s f a i dis_x-dis_{fa_{lca(x,y)}}+dis_{fa_i}-dis_{lca(x,y)}\ge dis_i-dis_{fa_i} disxdisfalca(x,y)+disfaidislca(x,y)disidisfai,移项得 d i s i − 2 d i s f a i ≤ d i s x − d i s l c a ( x , y ) − d i s f a l c a ( x , y ) dis_{i}-2dis_{fa_i}\le dis_x-dis_{lca(x,y)}-dis_{fa_{lca(x,y)}} disi2disfaidisxdislca(x,y)disfalca(x,y)

发现左边只与 i i i 有关,右边只与 x , y x,y x,y 有关,可以对每个节点建两个主席树维护 2 d i s i − d i s f a i 2dis_i-dis_{fa_i} 2disidisfai d i s i − 2 d i s f a i dis_i-2dis_{fa_i} disi2disfai,继承父亲的信息。查询时就先算出右边的值 A A A,在主席树上求 2 d i s i − d i s f a i 2dis_i-dis_{fa_i} 2disidisfai d i s i − 2 d i s f a i dis_i-2dis_{fa_i} disi2disfai 小于等于 A A A 的个数。时间复杂度 O ( ( n + q ) log ⁡ V ) O((n+q)\log V) O((n+q)logV)

考虑在线做。发现根是谁不重要,那么每次连边的时候,就直接让一边的树根当另一边的根的儿子即可,要用启发式合并保证复杂度,被合并的数要 dfs 一遍更新信息。时间复杂度 O ( n log ⁡ n log ⁡ V + q log ⁡ V ) O(n\log n\log V+q\log V) O(nlognlogV+qlogV),主席树空间能开多大就开多大。

代码如下

#include
using namespace std;
const int N=1e5+1,INF=1e9;
int n,Q,a[N];
int head[N],nxt[N<<1],to[N<<1],cnt,num;
int sz[N],dep[N],cnt1,dis[N];
int f[N][18],r[2][N],Cnt;
struct node
{
    int x,y;
}q[N];
struct Node
{
    int ls,rs,sz;
}tr[N*1300];
void insert(int &rt,int la,int l,int r,int x)
{
    rt=++Cnt;
    tr[rt]=tr[la];
    tr[rt].sz++;
    if(l==r) return;
    int mid=l+r>>1;
    if(x<=mid) insert(tr[rt].ls,tr[la].ls,l,mid,x);
    else insert(tr[rt].rs,tr[la].rs,mid+1,r,x);
}
int query(int rt,int l,int r,int k)
{
    if(r<=k) return tr[rt].sz;
    int mid=l+r>>1,sum=query(tr[rt].ls,l,mid,k);
    if(k>mid) sum+=query(tr[rt].rs,mid+1,r,k);
    return sum;
}
void add(int u,int v)
{
    to[++cnt]=v;
    nxt[cnt]=head[u];
    head[u]=cnt;
}
void dfs(int u,int Fa)
{
    dep[u]=dep[Fa]+1;
    dis[u]=dis[Fa]+a[u];
    f[u][0]=Fa;
    for(int i=1;i<=17;i++) f[u][i]=f[f[u][i-1]][i-1];
    insert(r[0][u],r[0][Fa],-INF,INF,2*dis[u]-dis[Fa]);
    insert(r[1][u],r[1][Fa],-INF,INF,dis[u]-2*dis[Fa]);
    for(int i=head[u];i;i=nxt[i]) if(to[i]!=Fa) dfs(to[i],u);
}
int getrt(int x)
{
    for(int i=17;i>=0;i--) if(f[x][i]) x=f[x][i];
    return x;
}
int getlca(int u,int v)
{
    if(dep[u]<dep[v]) swap(u,v);
    int dis=dep[u]-dep[v];
    for(int i=0;i<=17;i++) if(dis>>i&1) u=f[u][i];
    if(u==v) return u;
    for(int i=17;i>=0;i--) if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
    return f[u][0];
}
int read()
{
    int sum=0,c=getchar();
    while(c<48||c>57) c=getchar();
    while(c>=48&&c<=57) sum=sum*10+c-48,c=getchar();
    return sum;
}
int main()
{
    freopen("tour.in","r",stdin);
    freopen("tour.out","w",stdout);
    cin.tie(0)->sync_with_stdio(0);
    int type;
    cin>>type>>n>>Q;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        sz[i]=1;
        dis[i]=a[i];
        insert(r[0][i],0,-INF,INF,2*dis[i]);
        insert(r[1][i],0,-INF,INF,dis[i]);
    }
    int ans=0;
    for(int i=1,op,x,y;i<=Q;i++){
        cin>>op>>x>>y;
        if(type) x^=ans,y^=ans;
        if(op){
            int lca=getlca(x,y);
            ans=query(r[0][x],-INF,INF,dis[x]);
            ans-=query(r[0][f[lca][0]],-INF,INF,dis[x]);
            ans+=query(r[1][y],-INF,INF,dis[x]-dis[lca]-dis[f[lca][0]]);
            ans-=query(r[1][lca],-INF,INF,dis[x]-dis[lca]-dis[f[lca][0]]);
            cout<<ans<<"\n";
        }
        else{
            int rt1=getrt(x),rt2=getrt(y);
            if(sz[rt1]<sz[rt2]) swap(rt1,rt2),swap(x,y);
            sz[rt1]+=sz[rt2];
            dfs(y,x);
            add(x,y),add(y,x);
        }
    }
}

你可能感兴趣的:(算法)