SCOI2015 情报传递 主席树+LCA

哈哈哈哈老婆我有出息了,犬犬第一次从思路到代码都是自己一发切了紫题呢~

好了一眼数据结构。

考虑如何转化第i个时刻有威胁的情报员,若能产生威胁

则说明他们至少在i-c-1这个时刻"出生"

也就是转化为在权值线段树上查询[1,i-c-1]有多少个人。

启发了我们可以先把未来的情报员都弄下来,再记一个他们的生日,放到权值线段树上。

可是它是树上问题哎,怎么弄?

答:求个LCA,经典树上差分。

主席树的部分就是相当于把树拍成链,每个点在父亲节点的基础上,继续单点更新(或者不需要),维护从该点到根节点一共有多少人。

#include<bits/stdc++.h>
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
const int maxn=2e5+5;
const int N=maxn<<5;
int u[maxn];
struct tree{
    int ls,rs,sum;
}t[N];
int idx,rt[N];
vector<int>e[maxn];
struct query{
    int k,x,y,c;
}query[maxn];
int n,Q,people[maxn],dep[maxn],f[maxn][20],lg[maxn];
int update(int p,int pos,int l=1,int r=maxn){
    int q=++idx;
    t[q]=t[p];
    t[q].sum++;
    if(l==r){
        return q;
    }
    int mid=l+r>>1;
    if(pos<=mid) t[q].ls=update(t[p].ls,pos,l,mid);
    else t[q].rs=update(t[p].rs,pos,mid+1,r);
    return q;
}
void dfs(int u,int fa){
    // lca 
    if(people[u]){
        rt[u]=update(rt[fa],people[u]);
        //cout<<t[rt[u]].sum<<endl;
    }
    else {
        rt[u]=rt[fa];
    }
    
    dep[u]=dep[fa]+1;
    f[u][0]=fa;
    for(int i=1;i<=lg[dep[u]];i++){
        f[u][i]=f[f[u][i-1]][i-1];
    }
    
    for(int i=0;i<e[u].size();i++){
        int to=e[u][i];
        if(to==fa) continue;
        dfs(to,u);
    }
}
int getLca(int u, int v) {
    
    if (dep[u]<dep[v]) swap(u,v);
    for (int j=lg[dep[u]];j>=0;j--) {//让u,v到达同一高度
        if (dep[u]-(1<<j)>=dep[v]) {
            u=f[u][j];
        }
    }
    if (u==v) return u;
    for (int j=lg[dep[u]];j>=0;j--) {
        if (f[u][j]!=f[v][j]) {
            u=f[u][j];
            v=f[v][j];
        }
    }
    return f[u][0];
}
void init(int n){
    for(int i=2;i<=n;i++){
        lg[i]=lg[i/2]+1;
    }
}
int ask(int x,int y,int lca,int fa,int ql,int qr,int l=1,int r=maxn){
    if(ql>qr) return 0;
    //cout<<"l: "<<l<<" r: "<<qr<<" "<<t[x].sum<<" "<<t[y].sum<<" "<<t[lca].sum<<endl;
    if(ql<=l&&r<=qr) {
        /*cout<<"at [l,r]"<<ql<<" "<<qr<<" "<<l<<" "<<r<<endl;
        cout<<"return: "<<endl;
        cout<<"tx: "<<t[x].sum<<endl;
        cout<<"ty: "<<t[y].sum<<endl;
        cout<<"lca: "<<t[lca].sum<<endl;
        cout<<"fa: "<<t[fa].sum<<endl;
        cout<<"tot: "<<t[x].sum+t[y].sum-t[lca].sum-t[fa].sum<<endl;
        */
        return t[x].sum+t[y].sum-t[lca].sum-t[fa].sum;
    }
    int ans=0,mid=l+r>>1;
    if(ql<=mid) ans+=ask(t[x].ls,t[y].ls,t[lca].ls,t[fa].ls,ql,qr,l,mid);
    if(qr>mid) ans+=ask(t[x].rs,t[y].rs,t[lca].rs,t[fa].rs,ql,qr,mid+1,r); 
    //cout<<"l: "<<l<<" r: "<<r<<" "<<ans<<endl;
    return ans;
}
int main(){
    //freopen("lys.in","r",stdin);
    fastio;
    cin>>n;
    init(n);
    int root;
    for(int i=1;i<=n;i++){
        cin>>u[i];
        if(u[i]==0) root=i;
        else e[u[i]].push_back(i);
    }
    cin>>Q;
    for(int i=1;i<=Q;i++){
        cin>>query[i].k;
        if(query[i].k==1){
            cin>>query[i].x>>query[i].y>>query[i].c;
        }
        else {
            int ti;cin>>ti;
            people[ti]=i;// i-th day ti is actived
        }
    }
    dfs(root,0);
    for(int i=1;i<=Q;i++){
        if(query[i].k==2) continue;
        int lca=getLca(query[i].x,query[i].y);
        int fa_lca=f[lca][0];
        //cout<<":: q"<<query[i].x<<" "<<query[i].y<<" "<<lca<<" "<<fa_lca<<endl;
        //cout<<"birth-day before: "<<i-query[i].c-1<<endl;
        cout<<dep[query[i].x]+dep[query[i].y]-dep[lca]-dep[fa_lca]<<" "<<ask(rt[query[i].x],rt[query[i].y],rt[lca],rt[fa_lca],1,i-query[i].c-1)<<endl;// Birth < i-c-1
    }
}

 

-------------------

喜欢自己做oj琢磨怎么过题,就像梦回那年夏天,我苦苦调着bug,为第一次做出蓝题而兴奋着。

其他的都是后话了。

posted @ 2023-03-23 10:55  liyishui  阅读(6)  评论(0编辑  收藏  举报