CodeChef - TREEQUER

题目链接

给定一棵 \(n\) 个点的树,按照节点编号从小到大 dfs,\(q\) 次询问给定 \(k\) 个无交区间 \([l,r],[l_1,r_1],\cdots,[l_k,r_k]\) 求保留 dfs 序在任意区间中的点的导出子图的连通块数。

\(n,q,\sum k\le 5\times 10^5\)


提供一种考场上摸出来比官解好想些的做法。

考虑点边容斥。点数的计算是容易的,对边计数。考虑从左到右枚举区间,当前区间需要查询所有左端点在之前区间,右端点在当前区间的边数。

考虑在 dfs 序上有什么性质,钦定所有边 \((u,v)\)\(dfn\) 小的连向大的,将所有边挂在右端点处理。发现所有边两两间必定包含或无交,则所有右端点在当前考虑区间 \([l,r]\) 不合法的边左端点必定随右端点递增而递减。

假设当前考虑到区间 \([l,r]\),下一询问区间为 \([L,R]\)。此时考虑所有左端点在 \((r,L)\) 的边的最大右端点 \(v\) 在哪。

  • 定义 \(\mathrm{calc}(x,y)=\sum_{u,v\in [x,y]}[(u,v)\in E]\)
  1. \(v<L\)
图片名称

\(\;\)

发现此时不合法区间 \((r,L)\) 对之后的所有区间都没有影响,(设连向 \(v\) 边对应的左端点为 \(u\),则 \((r,u)\) 必定不会有边连向 \(\ge L\) 的位置,\((v,L)\) 中没有边连出,同样的可以得出所有任意一段在 \((r,L)\) 中的边必定两端都在其中)。则考虑直接合并 \([l,r],[L,R]\),算出其中的合法边数 \(\mathrm{calc}(l,R)-\mathrm{calc}(r+1,L-1)\),于是可以正常地与之后的区间处理。

  1. \(v>L\)
图片名称

\(\;\)

显然 \((1,u)\) 不会向 \((u,v)\) 中连边,\((u,v)\) 也不会向 \((v,n)\) 外连边,于是发现 \((u,v)\) 相对外面独立,于是考虑将 \((u,v)\) 中的询问区间单独拉出来递归处理,而对于没有被完全包含的区间 \([l',r']\),将蓝色部分拉出即可。

而考虑剩下部分,将 \([l,r],(r,u]\) 合并,合法边数 \(\mathrm{calc}(l,u)-\mathrm{calc}(r+1,u)\) 之后接上 \(v\) 及之后的部分正常处理即可。此时还需要考虑 \(u\in [l',v],v\in[v,r']\) 的边数,否则会算少,还有若最后一个被跨过的询问区间 \([l',r'],r'<v\)\(v\) 需要与 \(r'\)\(\mathrm{chkmin}\)

每个区间只会被处理一次,\(\mathrm{calc}\) 主席树实现能做到 \(O(\log n)\) 查询,则总复杂度 \(O((\sum k)\log n)\) 与官解相同。

代码维护的是之前合并区间中需要去掉的不合法边数 \(del\),实现大致相同。

Takanashi Rikka
#include<bits/stdc++.h>
using namespace std;
#define fin(x) freopen(#x".in","r",stdin)
#define fout(x) freopen(#x".out","w",stdout)
#define fr(x) fin(x),fout(x);
#define Fr(x,y) fin(x),fout(y)
#define INPUT(_1,_2,FILE,...) FILE
#define IO(...) INPUT(__VA_ARGS__,Fr,fr)(__VA_ARGS__)
#define mp make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define il inline
#define cfast ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
#define ll long long
#define ull unsigned long long
#define ve vector
#define intz(x,y) memset((x),(y),sizeof((x)))
template<typename Type>il void cmx(Type &x,Type y){if(y>x)x=y;}
template<typename Type>il void cmn(Type &x,Type y){if(y<x)x=y;}
#define lowbit(x) (x&-x)
#define pcount(x) __builtin_popcountll(x)
const int N=5e5+5,inf=1e9;
int n,q,OP,lst,L[N],R[N],sum[N],F[N],del[N];
ve<int>e[N];
int rt[N],tot;
struct node{int ls,rs,x;}t[N<<8];
void pushup(int u){t[u].x=t[t[u].ls].x+t[t[u].rs].x;}
void upd(int &id,int idx,int l,int r,int x,int d){
    t[id=++tot]=t[idx];
    if(l==r)return t[id].x+=d,void();
    int mid=l+r>>1;
    if(x<=mid)
         upd(t[id].ls,t[idx].ls,l,mid,x,d);
    else upd(t[id].rs,t[idx].rs,mid+1,r,x,d);
    pushup(id);
}
int query(int id,int idx,int l,int r,int L,int R){
    if(l>=L&&r<=R)return t[idx].x-t[id].x;
    int mid=l+r>>1,res=0;
    if(L<=mid)res+=query(t[id].ls,t[idx].ls,l,mid,L,R);
    if(R>mid) res+=query(t[id].rs,t[idx].rs,mid+1,r,L,R);
    return res;
}
int calc(int L,int R){
    if(L>R)return 0;
    return query(rt[L-1],rt[R],1,n,L,R);
}
int dfn[N],ct,f[N][25],lg[N],idx[N];
int get(int L,int R){
    if(L>R)return 0;int len=lg[R-L+1];
    return max(f[L][len],f[R-(1<<len)+1][len]);
}
ve<int>w[N];
void dfs(int u){
    idx[dfn[u]=++ct]=u;
    for(int v:e[u])if(!dfn[v])
        dfs(v),F[v]=u,w[dfn[v]].pb(dfn[u]),f[dfn[u]][0]=dfn[v];
}
int solve(ve<pii>&a,int l,int r,int beg,int ed){
    if(l>r)return 0;
    for(int i=l;i<=r;i++)
        L[i]=max(beg,a[i].fi),
        R[i]=min(ed,a[i].se);
    if(l==r)return calc(L[l],R[l]);
    int res=0;
    for(int i=l;i<=r;i++)del[i]=0,res+=(sum[i]=calc(L[i],R[i]));
    for(int i=l;i<r;i++){
        int nxt=get(R[i]+1,L[i+1]-1);
        if(nxt<a[i+1].fi){
            res-=sum[i]+sum[i+1];
            if(!nxt)nxt=L[i+1]-1;
            del[i+1]=del[i]+calc(L[i],L[i+1]-1)-calc(L[i],R[i]);
            res+=(sum[i+1]=calc(L[i],R[i+1])-del[i+1]);
            L[i+1]=L[i];
        }else{
            int id=upper_bound(a.begin(),a.end(),mp(nxt,inf))-a.begin();
            for(int j=i+1;j<id;j++)res-=sum[j];
            res+=solve(a,i+1,id-1,L[i+1],min(nxt,R[id-1]));
            if(nxt>=R[id-1])nxt=R[id-1];
            R[id-1]=min(a[id-1].se,ed);
            res-=sum[i];
            del[id-1]=del[i]+calc(L[i],nxt)-calc(L[i],R[i]);
            res+=(sum[id-1]=calc(L[i],R[id-1])-del[id-1]);
            L[id-1]=L[i],i=id-2;
        }
    }
    return res;
}
void UesugiErii(){
    cin>>n>>q>>OP;
    for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
    for(int i=1,u,v;i<n;i++)
        cin>>u>>v,e[u].pb(v),e[v].pb(u);
    for(int i=1;i<=n;i++)sort(e[i].begin(),e[i].end());
    dfs(1);
    for(int i=1;i<=n;i++){
        rt[i]=rt[i-1];
        for(int j:w[i])upd(rt[i],rt[i],1,n,j,1);
    }
    for(int i=1;i<25;i++)
        for(int j=1;j+(1<<i)-1<=n;j++)
            f[j][i]=max(f[j][i-1],f[j+(1<<i-1)][i-1]);
    ve<pii>tmp;
    while(q--){
        int k,sum=0;cin>>k,tmp.clear(),tmp.resize(k);
        for(int i=0;i<k;i++){
            cin>>tmp[i].fi>>tmp[i].se;
            if(OP)tmp[i].fi^=lst,tmp[i].se^=lst;
            sum+=tmp[i].se-tmp[i].fi+1;
        }
        cout<<(lst=sum-solve(tmp,0,k-1,1,n)-1)<<'\n';
    }
}
signed main(){
    // IO(fructus);
    cfast;
    int _=1;//cin>>_;
    for(;_;_--)UesugiErii();
    return 0;
}
posted @ 2026-02-04 22:09  Uesugi1  阅读(2)  评论(0)    收藏  举报