每日一题 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;
}
查看20道真题和解析