每日一题 dfs序专题 总结

Military Problem
简单题,我们预处理出dfs序,然后查询的时候之间判断一下sz的大小,就可以了!
代码:

#include<bits/stdc++.h>
#define fgx cerr<<"-----------------------"<<endl
#define LL long long
#define DB double
#define pb push_back
using namespace std;
inline int read(){
    int nm=0,fh=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') fh=-1;
    for(;isdigit(c);c=getchar()) nm=nm*10+c-'0';
    return nm*fh;
}
#define M 200020
int n,q,dfn[M],cnt,rev[M],sz[M]; vector<int>vec[M];
inline void dfs(int u){
    dfn[u]=++cnt,rev[cnt]=u,sz[u]=1;
    for(int i=0,TP=vec[u].size();i<TP;i++) dfs(vec[u][i]),sz[u]+=sz[vec[u][i]];
}
int main(){
    n=read(),q=read();
    for(int i=2;i<=n;i++){int x=read(); vec[x].pb(i);}
    for(int i=1;i<=n;i++) sort(vec[i].begin(),vec[i].end());
    dfs(1);
    while(q--){
        int x=read(),k=read();
        if(sz[x]<k) puts("-1"); else printf("%d\n",rev[dfn[x]+k-1]);
    }
    return 0;
}

选点
这道题是一个好题,思路非常妙,我们考虑假设我们在树上选了若干个点,然后我们按照着题目给出的大小关系去dfs,然后这个dfs序是单调增的,同时这个选出来的权值也是单调增的,这样就可以启发我们直接搞出dfs序,然后哦根据dfs序求出新的val,然后针对这个序列求一个最长上升子序列就可以了!
这个思路非常新颖,我们在类似的选点问题也可以采用一样的办法。
代码:

#include<bits/stdc++.h>
#define fgx cerr<<"-----------------------"<<endl
#define LL long long
#define DB double
#define pb push_back
using namespace std;
inline int read(){
    int nm=0,fh=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') fh=-1;
    for(;isdigit(c);c=getchar()) nm=nm*10+c-'0';
    return nm*fh;
}
#define M 100020
int n,val[M],ls[M],rs[M],a[M],cnt,sta[M],top;
inline void dfs(int u){
    a[++cnt]=val[u];
    if(rs[u]) dfs(rs[u]); if(ls[u]) dfs(ls[u]);
}
int main(){
    n=read();
    for(int i=1;i<=n;i++) val[i]=read();
    for(int i=1;i<=n;i++) ls[i]=read(),rs[i]=read();
    dfs(1);
    for(int i=1;i<=n;i++){
        if(!top||a[i]>sta[top]){sta[++top]=a[i]; continue;}
        int pos=lower_bound(sta+1,sta+top+1,a[i])-sta;
        sta[pos]=a[i];
    } printf("%d\n",top);
    return 0;
}

Colorful Tree
这题要求你求生成树的大小,我们发现这个大小问题其实和https://www.luogu.com.cn/problem/P3320寻宝游戏
这道题很像只是这道题从第一个点走到最后一个点,再回来,那么容易发先每条边都被走过两遍,对于这道题我们把最后的结果除以2就可以了,
我们针对每种颜色分别维护一下,支持插入和删除,修改的时候先删除再插入即可,注意需要注意空集的特判。
代码:

#include<bits/stdc++.h>
#define fgx cerr<<"-----------------------"<<endl
#define LL long long
#define DB double
#define pb push_back
using namespace std;
inline int read(){
    int nm=0,fh=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') fh=-1;
    for(;isdigit(c);c=getchar()) nm=nm*10+c-'0';
    return nm*fh;
}
#define M 100020
int n,tot,q,a[M],to[M<<1],nt[M<<1],fs[M];
int dep[M],fa[M][21],dfn[M],cnt,rev[M];
inline void link(int x,int y){to[++tot]=y,nt[tot]=fs[x],fs[x]=tot;}
inline int lca(int x,int y){
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=20;~i;i--) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
    for(int i=20;~i;i--) if(fa[x][i]^fa[y][i]) x=fa[x][i],y=fa[y][i];
    return (x==y)?x:fa[x][0]; 
}
inline int getdis(int x,int y){return dep[x]+dep[y]-(dep[lca(x,y)]<<1);}
inline void dfs(int x,int last=0){
    dep[x]=dep[last]+1,dfn[x]=++cnt,rev[cnt]=x,fa[x][0]=last;
    for(int i=1;i<=20;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
    for(int i=fs[x];i;i=nt[i]) if(to[i]^last) dfs(to[i],x);
}
#define IT set<int>::iterator
struct Sol{
    set<int>s; LL all;
    inline int fdnxt(int x){
        if(s.empty()) return -1;
        IT it=s.upper_bound(x); 
        return (it==s.end())?-1:rev[*it];
    }
    inline int fdpre(int x){
        if(s.empty()) return -1;
        IT it=s.lower_bound(x); return (it==s.begin())?-1:rev[*(--it)];
    }
    inline void Ins(int x){
        int pos=dfn[x],nx=fdnxt(pos),px=fdpre(pos);
        if((~nx)&&(~px)) all-=(LL)getdis(nx,px);
        if(~nx) all+=(LL)getdis(nx,x);
        if(~px) all+=(LL)getdis(px,x);
        s.insert(pos);
    }
    inline void Del(int x){
        int pos=dfn[x],nx=fdnxt(pos),px=fdpre(pos);
        if((~nx)&&(~px)) all+=(LL)getdis(nx,px);
        if(~nx) all-=(LL)getdis(nx,x);
        if(~px) all-=(LL)getdis(px,x);
        s.erase(pos); 
    }
    inline LL gtans(){
        if(s.empty()) return -1ll;
        return (all+getdis(rev[*s.begin()],rev[*s.rbegin()]))/2ll;
    }
    inline void debug(){
        for(IT it=s.begin();it!=s.end();it++)
            printf("%d ",*it);
        printf("\n");
    }
}P[M];

int main(){
    n=read();
    for(int i=1,x,y;i<n;i++) x=read(),y=read(),link(x,y),link(y,x);
    dfs(1); for(int i=1;i<=n;i++) a[i]=read(),P[a[i]].Ins(i);
    q=read();
    while(q--){
        char op[15]; scanf("%s",op);
        if(op[0]=='U'){
            int x=read(),y=read();
            P[a[x]].Del(x),a[x]=y,P[a[x]].Ins(x);
        } else{int x=read(); printf("%lld\n",P[x].gtans());}
        //for(int i=1;i<=3;i++) P[i].debug();
    }
    return 0;
}

Tree Requests
一道处理子树内部的问题,子树内部的处理通常我们需要用dfs序,然后如果可以离线我们可以用分治,莫队等算法在dfs序上进行,强制在线可以用一些主席树线段树分块等算法。
但是看到这道题,我写了一个和dfs序没有太大关系的dsu on the tree(之后作者应该会写一个dsu on the tree的总结)
然后我们可以用二进制优化,具体我们可以见代码
代码:

#include<bits/stdc++.h>
#define fgx cerr<<"-----------------------"<<endl
#define LL long long
#define DB double
#define pb push_back
using namespace std;
inline int read(){
    int nm=0,fh=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') fh=-1;
    for(;isdigit(c);c=getchar()) nm=nm*10+c-'0';
    return nm*fh;
}
#define M 500020
#define pii pair<int,int>
#define mp make_pair
#define fr first
#define sc second
int n,q,tot,to[M<<1],nt[M<<1],fs[M],ans[M];
int fa[M],dep[M],sz[M],mxs[M],hv[M],typ;
char ch[M]; vector<pii >vec[M];
inline void link(int u,int v){to[++tot]=v,nt[tot]=fs[u],fs[u]=tot;}
inline void dfs(int u){
    dep[u]=dep[fa[u]]+1,sz[u]=1;
    for(int i=fs[u];i;i=nt[i]) if(to[i]^fa[u]){
        dfs(to[i]),sz[u]+=sz[to[i]];
        if(sz[to[i]]>sz[mxs[u]]) mxs[u]=to[i];
    }
}
inline void chg(int u,int Lim){
    if(typ==-1) hv[dep[u]]=0; else hv[dep[u]]^=(1<<(ch[u]-'a'));
    for(int i=fs[u];i;i=nt[i]) if((to[i]^fa[u])&&(to[i]^Lim)) chg(to[i],Lim);
}
inline bool Judge(int x){return __builtin_popcount(x)<2;}
inline void dfst(int u,int flag=0){
    for(int i=fs[u];i;i=nt[i]) if((to[i]^fa[u])&&(to[i]^mxs[u])) dfst(to[i],1);
    if(mxs[u]) dfst(mxs[u],0);
    typ=1,chg(u,mxs[u]);
    for(int i=0,TP=vec[u].size();i<TP;i++) ans[vec[u][i].sc]=Judge(hv[vec[u][i].fr]);
    if(flag) typ=-1,chg(u,0);
}
int main(){
    n=read(),q=read();
    for(int i=2;i<=n;i++) fa[i]=read(),link(fa[i],i);
    scanf("%s",ch+1),dfs(1);
    for(int i=1;i<=q;i++){
        int x=read(),y=read();
        vec[x].pb(mp(y,i));
    } dfst(1);
    for(int i=1;i<=q;i++) puts(ans[i]?"Yes":"No");
    return 0;
}

求和
一道裸题,我们直接树状数组的基本操作放到dfs序上就可以了

#include<bits/stdc++.h>
#define fgx cerr<<"-----------------------"<<endl
#define LL long long
#define DB double
#define pb push_back
using namespace std;
inline int read(){
    int nm=0,fh=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') fh=-1;
    for(;isdigit(c);c=getchar()) nm=nm*10+c-'0';
    return nm*fh;
}
#define M 1000020
int n,m,rt,val[M],tot,to[M<<1],nt[M<<1],fs[M],L[M],R[M],cnt; LL sum[M];
inline void link(int u,int v){to[++tot]=v,nt[tot]=fs[u],fs[u]=tot;}
inline void dfs(int u,int last=0){
    L[u]=++cnt;
    for(int i=fs[u];i;i=nt[i]) if(to[i]^last) dfs(to[i],u);
    R[u]=cnt;
}
inline void mdf(int x,int y){for(int i=x;i<=n;i+=(i&-i)) sum[i]+=(LL)y;}
inline LL qry(int x,int y){
    LL ret=0;
    for(int i=y;i;i-=(i&-i)) ret+=sum[i];
    for(int i=x-1;i;i-=(i&-i)) ret-=sum[i];
    return ret;
}
int main(){
    n=read(),m=read(),rt=read();
    for(int i=1;i<=n;i++) val[i]=read();
    for(int i=1;i<n;i++){int u=read(),v=read(); link(u,v),link(v,u);}
    dfs(rt); for(int i=1;i<=n;i++) mdf(L[i],val[i]);
    while(m--){
        int opt=read(),x=read();
        if(opt==1){int y=read(); mdf(L[x],y);}
        else printf("%lld\n",qry(L[x],R[x]));
    }
    return 0;
}

Propagating tree
其实这道题的思维难度并不高,
这道题的操作虽然感觉很麻烦,但是发现它操作的本质无非就是对于奇偶分别讨论,就这么两种情况,那么我们针对奇偶分别维护一个树状数组就可以了,和上面的题感觉本质差不多。
代码:

 #include<bits/stdc++.h>
#define fgx cerr<<"-----------------------"<<endl
#define LL long long
#define DB double
#define pb push_back
using namespace std;
inline int read(){
    int nm=0,fh=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') fh=-1;
    for(;isdigit(c);c=getchar()) nm=nm*10+c-'0';
    return nm*fh;
}
#define M 200020
int n,m,tot,to[M<<1],nt[M<<1],fs[M],a[M],L[M],R[M],cnt,dep[M];
inline void link(int u,int v){to[++tot]=v,nt[tot]=fs[u],fs[u]=tot;}
inline void dfs(int u,int last=0){
    L[u]=++cnt,dep[u]=dep[last]+1;
    for(int i=fs[u];i;i=nt[i]) if(to[i]^last) dfs(to[i],u);
    R[u]=cnt;
}
struct BIT{
    int sum[M];
    inline void mdf(int x,int y){for(int i=x;i<=n;i+=(i&-i)) sum[i]+=y;}
    inline int qry(int x){int ret=0; for(int i=x;i;i-=(i&-i)) ret+=sum[i]; return ret;}
}P[2];
int main(){
    n=read(),m=read();
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=1;i<n;i++){int u=read(),v=read(); link(u,v),link(v,u);}
    dfs(1);
    while(m--){
        int opt=read(),x=read();
        if(opt==1){
            int y=read(),td=dep[x]&1;
            P[td].mdf(L[x],y),P[td].mdf(R[x]+1,-y);
            P[td^1].mdf(L[x],-y),P[td^1].mdf(R[x]+1,y);
        }else printf("%d\n",P[dep[x]&1].qry(L[x])+a[x]);
    }
    return 0;
}
全部评论

相关推荐

评论
2
1
分享
牛客网
牛客企业服务