简介

虚树,即剔除所有无关结点,只保留询问点和询问点的相关结点(两两之间的LCA),建一棵新树,这棵新树就是虚树。通过虚树,可以有效的减小询问(甚至修改)的复杂度。设询问点的个数是\(k\),那么建虚树的一般方法的时间复杂度为\(O(k \log k)\)。

构建方法

  1. 把所有询问点按dfs序排个序。

  2. 求出所有相邻结点的LCA(相关点)加入数组,结束后把根结点(\(1\))也加入数组。

  3. 再把所有询问点和相关点按dfs序排个序。

  4. 用栈维护虚树上根结点出发的一条链,按dfs序逐个插入结点,弹栈时连虚树边。

  5. 逐个弹出栈中剩余结点,并同时连虚树边。

代码

void build_itree(){
rin(i,1,cnt) arr[++len]=uu[i],arr[++len]=vv[i];
std::sort(arr+1,arr+len+1,cmp);
len=std::unique(arr+1,arr+len+1)-arr-1;
int temp=len;
rin(i,1,temp-1) arr[++len]=lca(arr[i],arr[i+1]);
arr[++len]=1;
std::sort(arr+1,arr+len+1,cmp);
len=std::unique(arr+1,arr+len+1)-arr-1;
rin(i,1,len) id2[arr[i]]=i;
top=0;
rin(i,1,len){
ini[arr[i]]=true;
while(top&&id[sta[top]]+siz[sta[top]]-1<id[arr[i]]) add_iedge(id2[sta[top-1]],id2[sta[top]]),top--;
sta[++top]=arr[i];
}
while(top>1) add_iedge(id2[sta[top-1]],id2[sta[top]]),top--;
}

[BZOJ2286][SDOI2011]消耗战

分析

模板题。对于每个询问,建出虚树后在虚树上跑树形DP(当时并不会建虚树所以直接抄的题解,建虚树的方法可能和上面讲的不太一样)即可。

代码

#include <bits/stdc++.h>
#define rin(i,a,b) for(register int i=(a);i<=(b);++i)
#define irin(i,a,b) for(register int i=(a);i>=(b);--i)
#define trav(i,a) for(register int i=head[a];i;i=e[i].nxt)
typedef long long LL;
using std::cin;
using std::cout;
using std::endl; inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*f;
} const int MAXN=250005;
int n,m,k,ecnt,head[MAXN];
int fa[MAXN],dep[MAXN],siz[MAXN],pc[MAXN],_top[MAXN],id[MAXN],tot;
int h[MAXN<<1],sta[MAXN],top;
LL f[MAXN],minw[MAXN];
std::vector<int> vec[MAXN];
struct Edge{
int to,nxt,w;
}e[MAXN<<1]; inline void add_edge(int bg,int ed,int val){
++ecnt;
e[ecnt].nxt=head[bg];
e[ecnt].to=ed;
e[ecnt].w=val;
head[bg]=ecnt;
} void dfs1(int x,int pre,int depth){
fa[x]=pre;
dep[x]=depth;
siz[x]=1;
int maxsiz=-1;
trav(i,x){
int ver=e[i].to;
if(ver==pre) continue;
minw[ver]=std::min(minw[x],1ll*e[i].w);
dfs1(ver,x,depth+1);
siz[x]+=siz[ver];
if(siz[ver]>maxsiz){
maxsiz=siz[ver];
pc[x]=ver;
}
}
} void dfs2(int x,int topf){
_top[x]=topf;
id[x]=++tot;
if(!pc[x]) return;
dfs2(pc[x],topf);
trav(i,x){
int ver=e[i].to;
if(ver==fa[x]||ver==pc[x]) continue;
dfs2(ver,ver);
}
} inline int lca(int x,int y){
while(_top[x]!=_top[y]){
if(dep[_top[x]]<dep[_top[y]]) std::swap(x,y);
x=fa[_top[x]];
}
return dep[x]<dep[y]?x:y;
} inline bool cmp(int x,int y){
return id[x]<id[y];
} inline void add_iedge(int bg,int ed){
vec[bg].push_back(ed);
} void ins(int x){
if(top==1){
sta[++top]=x;
return;
}
int _lca=lca(x,sta[top]);
if(_lca==sta[top]) return;
while(top>1&&id[sta[top-1]]>=id[_lca]) add_iedge(sta[top-1],sta[top]),top--;
if(_lca!=sta[top]) add_iedge(_lca,sta[top]),sta[top]=_lca;
sta[++top]=x;
} void dfs3(int x){
if(!vec[x].size()){
f[x]=minw[x];
return;
}
f[x]=0;
rin(i,0,(int)vec[x].size()-1){
int ver=vec[x][i];
dfs3(ver);
f[x]+=std::min(f[ver],minw[ver]);
}
vec[x].clear();
} int main(){
n=read();
rin(i,2,n){
int u=read(),v=read(),w=read();
add_edge(u,v,w);
add_edge(v,u,w);
}
minw[1]=1e18;
dfs1(1,0,1);
dfs2(1,1);
m=read();
while(m--){
k=read();
rin(i,1,k) h[i]=read();
std::sort(h+1,h+k+1,cmp);
top=1,sta[1]=1;
rin(i,1,k) ins(h[i]);
while(top>1) add_iedge(sta[top-1],sta[top]),top--;
dfs3(1);
printf("%lld\n",f[1]);
}
return 0;
}

[CF1073G]Yet Another LCP Problem

分析

说白了就是给SAM的parent树建虚树。

众所周知,parent树是反串的后缀树。将字符串reverse(),建出后缀树。于是我们所求的问题转化为了给你两个点集,要求所有两个结点分别来自不同点集的点对的LCA的深度之和。

这个问题似乎可以用[BZOJ3626][LNOI2014]LCA的方法解决,不过我们还可以做到更快。考虑虚树上每条边的贡献,显然(?)是\(cnta[x] \times cntb[x] \times w[x]\),即子树内集合\(A\)的点的个数乘集合\(B\)的点的个数再乘这条边的边权(后缀树上这个转移的串长)。

代码

#include <bits/stdc++.h>
#define rin(i,a,b) for(register int i=(a);i<=(b);++i)
#define irin(i,a,b) for(register int i=(a);i>=(b);--i)
#define trav(i,a) for(register int i=head[a];i;i=e[i].nxt)
typedef long long LL;
using std::cin;
using std::cout;
using std::endl; inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*f;
} const int MAXN=200005;
int n,q,las,tot,ecnt,head[MAXN<<1];
int fa[MAXN<<1],dep[MAXN<<1],siz[MAXN<<1],pc[MAXN<<1],_top[MAXN<<1],id[MAXN<<1],totid;
int seta[MAXN<<1],setb[MAXN<<1],siza,sizb,arr[MAXN<<2],len,sta[MAXN<<1],top;
int frm[MAXN<<1],cnta[MAXN<<1],cntb[MAXN<<1];
char s[MAXN];
std::vector<int> vec[MAXN<<1];
struct sam{
int fa,to[26];
int len;
}a[MAXN<<1];
struct Edge{
int to,nxt;
}e[MAXN<<1]; inline void add_edge(int bg,int ed){
ecnt++;
e[ecnt].to=ed;
e[ecnt].nxt=head[bg];
head[bg]=ecnt;
} inline void add_iedge(int bg,int ed){
vec[bg].push_back(ed);
} void extend(int c,int np){
int p=las;las=np,a[np].len=a[p].len+1;
while(p&&!a[p].to[c]) a[p].to[c]=np,p=a[p].fa;
if(!p){a[np].fa=1;return;}
int q=a[p].to[c];
if(a[p].len+1==a[q].len){a[np].fa=q;return;}
int nq=++tot;a[nq]=a[q],a[nq].len=a[p].len+1,a[np].fa=a[q].fa=nq;
while(p&&a[p].to[c]==q) a[p].to[c]=nq,p=a[p].fa;
} void dfs1(int x,int pre,int depth){
fa[x]=pre;
dep[x]=depth;
siz[x]=1;
int maxsiz=-1;
trav(i,x){
int ver=e[i].to;
dfs1(ver,x,depth+1);
siz[x]+=siz[ver];
if(siz[ver]>maxsiz){
maxsiz=siz[ver];
pc[x]=ver;
}
}
} void dfs2(int x,int topf){
_top[x]=topf;
id[x]=++totid;
if(!pc[x]) return;
dfs2(pc[x],topf);
trav(i,x){
int ver=e[i].to;
if(ver==pc[x]) continue;
dfs2(ver,ver);
}
} inline int lca(int x,int y){
while(_top[x]!=_top[y]){
if(dep[_top[x]]<dep[_top[y]]) std::swap(x,y);
x=fa[_top[x]];
}
return dep[x]<dep[y]?x:y;
} inline bool cmp(int x,int y){
return id[x]<id[y];
} int main(){
n=read(),q=read(),las=1,tot=n+1;
scanf("%s",s+1);
irin(i,n,1) extend(s[i]-'a',i+1);
rin(i,2,tot) add_edge(a[i].fa,i);
dfs1(1,0,1);
dfs2(1,1);
while(q--){
LL ans=0;
siza=read(),sizb=read(),len=0;
rin(i,1,siza) arr[++len]=seta[i]=read()+1,cnta[seta[i]]=1;
rin(i,1,sizb) arr[++len]=setb[i]=read()+1,cntb[setb[i]]=1;
std::sort(arr+1,arr+len+1,cmp);
len=std::unique(arr+1,arr+len+1)-arr-1;
int temp=len;
rin(i,1,temp-1) arr[++len]=lca(arr[i],arr[i+1]);
arr[++len]=1;
std::sort(arr+1,arr+len+1,cmp);
len=std::unique(arr+1,arr+len+1)-arr-1;
top=0;
rin(i,1,len){
while(top&&id[sta[top]]+siz[sta[top]]-1<id[arr[i]]) add_iedge(sta[top-1],sta[top]),frm[sta[top]]=a[sta[top]].len-a[sta[top-1]].len,top--;
sta[++top]=arr[i];
}
rin(i,1,top-1) add_iedge(sta[i],sta[i+1]),frm[sta[i+1]]=a[sta[i+1]].len-a[sta[i]].len;
irin(i,len,1){
int x=arr[i];
rin(j,0,(int)vec[x].size()-1){
int ver=vec[x][j];
cnta[x]+=cnta[ver];
cntb[x]+=cntb[ver];
}
ans+=1ll*cnta[x]*cntb[x]*frm[x];
}
rin(i,1,len) cnta[arr[i]]=cntb[arr[i]]=0,vec[arr[i]].clear();
printf("%I64d\n",ans);
}
return 0;
}

[BZOJ5287][HNOI2018]毒瘤

分析

这题是真毒瘤,敲了2h+。

观察数据范围发现边数并不比点数多很多,建出一棵生成树后,最多只会余下\(11\)条边。所以可以先随便求出原图的一棵生成树,把多出来的边的相关结点揪出来搞一棵虚树,在虚树上的转移提前算好系数\(k[x][0/1][0/1]\),表示结点\(x\)选或不选对其虚树上的父亲选或不选的贡献的系数,最后枚举每条多出来的边的两端点的选取情况(可以通过强制其中一个端点选或不选来实现),做树形DP即可。

举例,考虑如何求\(k[x][0/1][0]\)(即不选结点\(x\)的贡献),容易想到虚树上的一条边对应原树上的一条链。令\(f[x][0]=1,f[x][1]=0\),这样可以确保算的只是不选结点\(x\)的贡献,单独把这条链拿出来做树形DP就好了,记得过程中算上分枝的贡献。求\(k[x][0/1][1]\)的过程类似。

实现复杂,代码超长,细节超多,调试困难,为出题人点赞。

代码

#include <bits/stdc++.h>
#define rin(i,a,b) for(register int i=(a);i<=(b);++i)
#define irin(i,a,b) for(register int i=(a);i>=(b);--i)
#define trav(i,a) for(register int i=head[a];i;i=e[i].nxt)
typedef long long LL;
using std::cin;
using std::cout;
using std::endl; struct Erkko_istream{
inline char getc(){
static const int IN_LEN=1<<18|1;
static char buf[IN_LEN],*s,*t;
return (s==t)&&(t=(s=buf)+fread(buf,1,IN_LEN,stdin)),s==t?-1:*s++;
}
template<typename _Tp> inline Erkko_istream & operator >> (_Tp &x){
static char c11,boo;
for(c11=getc(),boo=0;!isdigit(c11);c11=getc()){
if(c11==-1) return *this;
boo|=c11=='-';
}
for(x=0;isdigit(c11);c11=getc()) x=x*10+(c11^'0');
boo&&(x=-x);
return *this;
}
}io; const int MAXN=100005;
const LL MOD=998244353;
int n,m,ecnt,head[MAXN],uu[15],vv[15],cnt;
int fa[MAXN],dep[MAXN],siz[MAXN],pc[MAXN],_top[MAXN],id[MAXN],tot;
int arr[45],len,sta[45],top,id2[MAXN],must[45];
LL f[MAXN][2],k[45][2][2],ans;
bool vis1[MAXN],vis2[MAXN],ini[MAXN];
std::vector<int> vec[45];
struct Edge{
int to,nxt;
}e[(MAXN+10)<<1]; inline void add_edge(int bg,int ed){
ecnt++;
e[ecnt].to=ed;
e[ecnt].nxt=head[bg];
head[bg]=ecnt;
} inline void add_iedge(int bg,int ed){
vec[bg].push_back(ed);
} void dfs1(int x,int pre,int depth){
fa[x]=pre;
dep[x]=depth;
siz[x]=1;
int maxsiz=-1;
trav(i,x){
int ver=e[i].to;
if(ver==pre) continue;
if(siz[ver]){
if(x>ver) continue;
++cnt;
uu[cnt]=x;
vv[cnt]=ver;
continue;
}
dfs1(ver,x,depth+1);
siz[x]+=siz[ver];
if(siz[ver]>maxsiz){
maxsiz=siz[ver];
pc[x]=ver;
}
}
} void dfs2(int x,int topf){
_top[x]=topf;
id[x]=++tot;
if(!pc[x]) return;
dfs2(pc[x],topf);
trav(i,x){
int ver=e[i].to;
if(ver==fa[x]||ver==pc[x]) continue;
if(fa[ver]!=x) continue;
dfs2(ver,ver);
}
} inline int lca(int x,int y){
while(_top[x]!=_top[y]){
if(dep[_top[x]]<dep[_top[y]]) std::swap(x,y);
x=fa[_top[x]];
}
return dep[x]<dep[y]?x:y;
} inline bool cmp(int x,int y){
return id[x]<id[y];
} void build_itree(){
rin(i,1,cnt) arr[++len]=uu[i],arr[++len]=vv[i];
std::sort(arr+1,arr+len+1,cmp);
len=std::unique(arr+1,arr+len+1)-arr-1;
int temp=len;
rin(i,1,temp-1) arr[++len]=lca(arr[i],arr[i+1]);
arr[++len]=1;
std::sort(arr+1,arr+len+1,cmp);
len=std::unique(arr+1,arr+len+1)-arr-1;
rin(i,1,len) id2[arr[i]]=i;
top=0;
rin(i,1,len){
ini[arr[i]]=true;
while(top&&id[sta[top]]+siz[sta[top]]-1<id[arr[i]]) add_iedge(id2[sta[top-1]],id2[sta[top]]),top--;
sta[++top]=arr[i];
}
while(top>1) add_iedge(id2[sta[top-1]],id2[sta[top]]),top--;
} void dfs3(int x){
f[x][0]=f[x][1]=1;
trav(i,x){
int ver=e[i].to;
if(ver==fa[x]) continue;
if(fa[ver]!=x) continue;
dfs3(ver);
f[x][0]=f[x][0]*(f[ver][0]+f[ver][1])%MOD;
f[x][1]=f[x][1]*f[ver][0]%MOD;
}
} void getk(){
dfs3(1);
add_edge(0,1);
irin(i,len,1){
int x=arr[i],pre=fa[x],r=1;LL temp[2][2]={1,0,0,0};
while((!ini[x]||x==arr[i])&&x){
vis1[x]=true;r^=1;
if(x!=arr[i]) temp[r][0]=(temp[r^1][0]+temp[r^1][1])%MOD,temp[r][1]=temp[r^1][0];
trav(i,x){
int ver=e[i].to;
if(ver==fa[x]) continue;
if(fa[ver]!=x) continue;
if(vis1[ver]) continue;
temp[r][0]=temp[r][0]*(f[ver][0]+f[ver][1])%MOD;
temp[r][1]=temp[r][1]*f[ver][0]%MOD;
}
pre=fa[pre],x=fa[x];
}
x=arr[i];
k[id2[x]][0][0]=(temp[r][0]+temp[r][1])%MOD;
k[id2[x]][1][0]=temp[r][0];
pre=fa[x],r=1;
memset(temp,0,sizeof temp);
temp[0][1]=1;
while((!ini[x]||x==arr[i])&&x){
vis2[x]=true;r^=1;
if(x!=arr[i]) temp[r][0]=(temp[r^1][0]+temp[r^1][1])%MOD,temp[r][1]=temp[r^1][0];
trav(i,x){
int ver=e[i].to;
if(ver==fa[x]) continue;
if(fa[ver]!=x) continue;
if(vis2[ver]) continue;
temp[r][0]=temp[r][0]*(f[ver][0]+f[ver][1])%MOD;
temp[r][1]=temp[r][1]*f[ver][0]%MOD;
}
pre=fa[pre],x=fa[x];
}
x=arr[i];
k[id2[x]][0][1]=(temp[r][0]+temp[r][1])%MOD;
k[id2[x]][1][1]=temp[r][0];
}
} void dfs4(int x){
f[x][0]=f[x][1]=1;
rin(i,0,(int)vec[x].size()-1){
int ver=vec[x][i];
dfs4(ver);
f[x][0]=f[x][0]*((f[ver][0]*k[ver][0][0]+f[ver][1]*k[ver][0][1])%MOD)%MOD;
f[x][1]=f[x][1]*((f[ver][0]*k[ver][1][0]+f[ver][1]*k[ver][1][1])%MOD)%MOD;
}
if(must[x]==0) f[x][1]=0;
else if(must[x]==1) f[x][0]=0;
} void solve(){
rin(i,0,(1<<cnt)-1){
memset(must,-1,sizeof must);
bool flag=false;
rin(j,1,cnt){
int temp=((i>>(j-1))&1);
if(must[id2[uu[j]]]>-1&&must[id2[uu[j]]]!=temp){
flag=true;
break;
}
must[id2[uu[j]]]=temp;
if(must[id2[uu[j]]]==1){
if(must[id2[vv[j]]]==1){
flag=true;
break;
}
must[id2[vv[j]]]=0;
}
}
if(flag) continue;
dfs4(1);
ans=(ans+f[1][0]*k[1][0][0]+f[1][1]*k[1][0][1])%MOD;
}
} int main(){
#ifndef ONLINE_JUDGE
freopen("duliu.in","r",stdin);
freopen("duliu.out","w",stdout);
#endif
io>>n>>m;
rin(i,1,m){
int u,v;
io>>u>>v;
add_edge(u,v);
add_edge(v,u);
}
dfs1(1,0,1);
dfs2(1,1);
build_itree();
getk();
solve();
printf("%lld\n",ans);
return 0;
}

[CF966E]May Holidays

分析

虚树和根号重构!

原题可以转化为:

  1. 将结点\(x\)到根的所有结点权值\(+1/-1\),并给结点\(x\)打上/去掉标记。

  2. 查询有多少个结点的权值为负且没有打标记。

考虑根号重构,每根号个操作重构虚树。虚树边,也就是原树上和这根号个修改无关的链可以一起处理。对于一条无关链上的所有结点的权值,直接排序后unique(),记一下每种权值的个数,维护一个指针指向第一个权值\(\geq 0\)的位置,通过类似于打lazy标记的方式进行修改。修改的同时维护\(ans\)就好了。

也是超多细节,实现起来略微丧心病狂。

代码

#include <bits/stdc++.h>
#define rin(i,a,b) for(register int i=(a);i<=(b);++i)
#define irin(i,a,b) for(register int i=(a);i>=(b);--i)
#define trav(i,a) for(register int i=head[a];i;i=e[i].nxt)
typedef long long LL;
using std::cin;
using std::cout;
using std::endl; inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*f;
} const int MAXN=100005;
int n,m,ecnt,head[MAXN];
int fa[MAXN],dep[MAXN],siz[MAXN],pc[MAXN],_top[MAXN],id[MAXN],tot;
int t[MAXN],opt[505],cnt,arr[1005],len,sta[1005],top;
int ifa[MAXN],ptr[MAXN],tag[MAXN],ans;
bool ini[MAXN],onv[MAXN];
std::vector<int> buc[MAXN],num[MAXN];
struct Edge{
int to,nxt;
}e[MAXN]; inline void add_edge(int bg,int ed){
ecnt++;
e[ecnt].to=ed;
e[ecnt].nxt=head[bg];
head[bg]=ecnt;
} inline void add_iedge(int bg,int ed){
ifa[ed]=bg;
} void dfs1(int x,int pre,int depth){
fa[x]=pre;
dep[x]=depth;
siz[x]=1;
int maxsiz=-1;
trav(i,x){
int ver=e[i].to;
dfs1(ver,x,depth+1);
siz[x]+=siz[ver];
if(siz[ver]>maxsiz){
maxsiz=ver;
pc[x]=ver;
}
}
} void dfs2(int x,int topf){
_top[x]=topf;
id[x]=++tot;
if(!pc[x]) return;
dfs2(pc[x],topf);
trav(i,x){
int ver=e[i].to;
if(ver==pc[x]) continue;
dfs2(ver,ver);
}
} inline int lca(int x,int y){
while(_top[x]!=_top[y]){
if(dep[_top[x]]<dep[_top[y]]) std::swap(x,y);
x=fa[_top[x]];
}
return dep[x]<dep[y]?x:y;
} inline bool cmp(int x,int y){
return id[x]<id[y];
} void build_itree(){
rin(i,1,cnt) arr[i]=std::abs(opt[i]);
len=cnt;
std::sort(arr+1,arr+len+1,cmp);
len=std::unique(arr+1,arr+len+1)-arr-1;
int temp=len;
rin(i,1,temp-1) arr[++len]=lca(arr[i],arr[i+1]);
arr[++len]=1;
std::sort(arr+1,arr+len+1,cmp);
len=std::unique(arr+1,arr+len+1)-arr-1;
top=0;
rin(i,1,len){
ini[arr[i]]=true;
while(top&&id[sta[top]]+siz[sta[top]]-1<id[arr[i]]) add_iedge(sta[top-1],sta[top]),top--;
sta[++top]=arr[i];
}
while(top>1) add_iedge(sta[top-1],sta[top]),top--;
} void solve(){
rin(i,1,len){
int x=arr[i],cur=x;
while(x&&(!ini[x]||x==cur)){
if(x!=cur&&!onv[x]) buc[cur].push_back(t[x]);
x=fa[x];
}
sort(buc[cur].begin(),buc[cur].end());
rin(j,0,(int)buc[cur].size()-1){
if(j==0||buc[cur][j]!=buc[cur][j-1]){
num[cur].push_back(0),buc[cur][num[cur].size()-1]=buc[cur][j];
if(buc[cur][j]<0) ptr[cur]++;
}
++num[cur][num[cur].size()-1];
}
while(buc[cur].size()>num[cur].size()) buc[cur].pop_back();
}
rin(i,1,cnt){
int x=opt[i],cur=abs(x);
if(x>0){
onv[x]=true;
if(t[x]<0) ans--;
while(x){
t[x]--;tag[x]--;
if(ptr[x]!=buc[x].size()&&buc[x][ptr[x]]+tag[x]<0) ans+=num[x][ptr[x]],ptr[x]++;
if(t[x]==-1&&!onv[x]) ans++;
x=ifa[x];
}
}
else{
onv[x=-x]=false;
if(t[x]+1<0) ans++;
while(x){
t[x]++;tag[x]++;
if(ptr[x]&&buc[x][ptr[x]-1]+tag[x]>=0) --ptr[x],ans-=num[x][ptr[x]];
if(t[x]==0&&!onv[x]&&x!=cur) ans--;
x=ifa[x];
}
}
printf("%d ",ans);
}
rin(i,1,len){
int x=arr[i],cur=x;
while(x&&(!ini[x]||x==cur)){
if(x!=cur) t[x]+=tag[cur];
x=fa[x];
}
}
rin(i,1,len){
int x=arr[i];
buc[x].clear(),num[x].clear(),ifa[x]=ptr[x]=tag[x]=0,ini[x]=false;
}
len=0;
} int main(){
n=read(),m=read();
rin(i,2,n) add_edge(read(),i);
rin(i,1,n) t[i]=read();
dfs1(1,0,1);
dfs2(1,1);
int lim=sqrt(m)+0.5;
rin(i,1,m){
opt[++cnt]=read();
if(cnt==lim){
build_itree();
solve();
cnt=0;
}
}
if(cnt){
build_itree();
solve();
}
printf("\n");
return 0;
}

[CF1073G]Surprise me!

暂时不想写了ToT。

虚树总结&题单&简要题解的更多相关文章

  1. 动态淀粉质(划掉)题单&简要题解

    简介 动态点分治的思想:还不太清楚诶怎么办. 大概是通过降低树高来降低每次修改和询问的复杂度吧,还可以把树上一个连通块的信息统计到一个点(重心)上.具体实现方式和普通的静态点分治没有太大的区别,只是把 ...

  2. $FFT/NTT/FWT$题单&简要题解

    打算写一个多项式总结. 虽然自己菜得太真实了. 好像四级标题太小了,下次写博客的时候再考虑一下. 模板 \(FFT\)模板 #include <iostream> #include < ...

  3. [Bzoj2286][Sdoi2011]消耗战(虚树模板题附讲解)

    2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 4896  Solved: 1824[Submit][Statu ...

  4. 【BZOJ3879】SvT(后缀自动机,虚树)

    [BZOJ3879]SvT(后缀自动机,虚树) 题面 BZOJ 题解 看着这个东西,询问若干个前缀两两之间的\(lcp\)? 显然\(lcp\)就是\(SAM\)构建出来的\(parent\)数上的\ ...

  5. Codeforces 863 简要题解

    文章目录 A题 B题 C题 D题 E题 F题 G题 传送门 简要题解?因为最后一题太毒不想写了所以其实是部分题解... A题 传送门 题意简述:给你一个数,问你能不能通过加前导000使其成为一个回文数 ...

  6. 2018.09.25 bzoj3572: [Hnoi2014]世界树(虚树+树形dp)

    传送门 虚树入门题? 好难啊. 在学习别人的写法之后终于过了. 这道题dp方程很好想. 主要是不好写. 简要说说思路吧. 显然最优值只能够从子树和父亲转移过来. 于是我们先dfs一遍用儿子更新父亲,然 ...

  7. 【CTSC2018】暴力写挂(边分治,虚树)

    [CTSC2018]暴力写挂(边分治,虚树) 题面 UOJ BZOJ 洛谷 题解 发现第二棵树上的\(LCA\)的深度这玩意没法搞,那么枚举在第二棵树上的\(LCA\). 然后剩下的部分就是\(dep ...

  8. 洛谷P3233 世界树 [HNOI2014] 虚树

    正解:虚树 解题报告: 传送门! 首先看到这种就要想到虚树这个是毫无疑问的QwQ 建虚树什么的都可以循规蹈矩地做,不说辣,具体可以看下虚树学习笔记什么的看下板子 但是建好虚树之后怎么搞还是有点儿讲究, ...

  9. 【BZOJ4912】天才黑客(最短路,虚树)

    [BZOJ4912]天才黑客(最短路,虚树) 题面 BZOJ 洛谷 题解 \(Anson\)爷讲过的题目,然而我还是不会做 只有照着\(zsy\)的程序打我才会做....果然太弱了. 这道题目显然是把 ...

随机推荐

  1. (4.13)mysql备份原理(转)

    关键词:mysqldump原理,--single-transaction,mysql备份原理 转自:https://www.cnblogs.com/cchust/p/5452557.html MySQ ...

  2. Android快捷键大全

    参考来源:https://mp.weixin.qq.com/s/T809p17Wt8XHkbLwcQf9ow 1,Ctrl + J  快捷代码列表 2,Ctrl+Alt+O 这个快捷键可以自动导包或删 ...

  3. [.net core]5.outProcess

    与inProcess比较  OutProcess性能更差,因为此时它使用了两个web服务器  ,内部是kestrel  外部可能是iis apache nginx 等. 使用visual studio ...

  4. Deepin安装 ruby 包管理工具 RVM(适用于 Debian 系列)

    1. 安装 GPG keys 先安装 gpg2 工具 sudo apt install gnupg2 再安装 keys gpg2 --recv-keys 409B6B1796C275462A17031 ...

  5. 求x到y的最少计算次数 (BFS)

    时间限制:1秒 空间限制:262144K 给定两个-100到100的整数x和y,对x只能进行加1,减1,乘2操作,问最少对x进行几次操作能得到y? 例如:a=3,b=11: 可以通过3*2*2-1,3 ...

  6. window环境安装composer

    今天在下载symfony2的框架的时候,发现要用到composer,因为之前笔者完全没有接触过composer,所以研究了很久之后,才终于安装完成 由于网上有各种资料介绍如何安装composer的,但 ...

  7. 工具使用——使用XShell连接linux系统

    1.首先到官网取下载一个XShell安装包,根据提示安装成功. 2.打开软件,点击新建连接 3.在新建连接页面输入,主机名称.主机地址.端口号,点击确定按钮. 4.在弹出的会话窗口中,选中我们刚刚创建 ...

  8. linux如何配置使用sendEmail发送邮件

    sendEmail是一个轻量级.命令行的SMTP邮件客户端.如果你需要使用命令行发送邮件,那么sendEmail是非常完美的选择.使用简单并且功能强大.这个被设计用在php.bash.perl和web ...

  9. 011-通过安装percona插件监控MySQL

    percona-monitoring-plugins是percona专门为MySQL监控的工具,支持Nagios,cacti,zabibx,本文主要介绍percona-monitoring-plugi ...

  10. Java学习01-使用maven插件tomcat搭建web maven项目

    我使用社区版的IDEA,社区版的没有tomcat插件,只能使用maven插件进行tomcat的使用了,下面进入正题 一.pom.xml配置tomcat插件 <build> <fina ...