简介

虚树,即剔除所有无关结点,只保留询问点和询问点的相关结点(两两之间的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. [转帖] 国产x86-海光禅定 2018年营收过亿?

    中科曙光:全年业绩稳健,海光芯片营收过亿 X86服务器市场Intel占据绝对优势:X86处理器已经成为全球最广泛使用的处理器架构之一,尤其是在PC和服务器领域,其中在处理器市场的份额高达90%以上.中 ...

  2. java操作ElasticSearch(es)进行增删查改操作

    有时间是要了解一下ES这个东西的~ ---------------------------------------------------------------------------------- ...

  3. numpy-查找操作大全

    本文记录日常工作中遇到的查找操作,持续更新. 注意:输入必须是 数组,不能是 list 极值 min,max 返回极值 argmin(a, axis=None, out=None), 返回极值所在的位 ...

  4. java使用Callable创建又返回值的线程

    并发编程使我们可以将程序分为很多个分离的,相互之间独立的任务,通过使用多线程的机制,将每个任务都会有一个执行线程来单独的驱动,一个线程是 进程中一个单一顺序控制流,一个进程可以拥有多个线程,也就相当于 ...

  5. 剑指offer-3:跳阶梯

    三.跳台阶 题目描述 一只青蛙一次可以跳上1级台阶,也可以跳上2级.求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果). 分析 青蛙每一次跳跃只有两种选择:一是再跳1级阶梯到达第n ...

  6. jsp-TagLib标签库

    <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ t ...

  7. JDK12的安装搭建

    JDK12的安装搭建 一.JDK下载 ​ 1.JDK官网下载地址:https://www.oracle.com/technetwork/java/javase/downloads/jdk12-down ...

  8. jenkins 构建时显示git分支插件、显示构建分支插件

    参数化构建分支 1.安装插件:Git Parameter 2.找到我们在Jenkins中建立的工程,勾选“参数化构建过程”,并如下配置 3.在“源码管理”中如下配置 Jenkins构建完显示构建用户和 ...

  9. OOP三大核心封装继承多态

    OOP支柱 3 个核心:封装 继承 多态 封装就是将实现细节隐藏起来,也起到了数据保护的作用. 继承就是基于已有类来创建新类可以继承基类的核心功能. 在继承中 另外一种代码重用是:包含/委托,这种重用 ...

  10. inoutfy与rsync进行实时同步

    更新阿里epel源 安装镜像源 curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo --- 扩展 ...