虚树是解决一类树上问题的强力工具。
基本上是多组询问,每组询问给出一组特殊点,让你完成某个任务。。。。
将所有特殊点按照原树的dfs序排序,然后依次枚举每个特殊点,用增量法创建这组询问的虚树。
我们创建的时候维护一个栈,栈里的元素构成一条链。
当我们新加入一个点v的时候,先求出v与栈顶元素的lca。因为我们要保证栈中的元素是构成一条链的嘛。然后就要把 ,不在lca->v这条链上的点都弹出去。然后把v入栈。
中间弹点的时候记得要加边。
luoguP2495 [SDOI2011]消耗战
把虚树建出来之后直接dp即可。
#include
using namespace std;
typedef long long LL;
const int N = 1e6 + 10;
#define fi first
#define se second
#define pb push_back
#define wzh(x) cerr<<#x<<'='<
struct Virtual_tree {
vector<int>v[N], g[N];
int dep[N], f[N][22], st[N], top;
int n, r, dfn[N], CNT;
void init(int _n, int _r) {
n = _n; r = _r; CNT = 0;
for (int i = 1; i <= n; i++) {
v[i].clear();
g[i].clear();
}
}
int lca(int u, int v) {
if (dep[u] < dep[v])swap(u, v);
for (int i = 19; i >= 0; i--)if ((dep[u] - dep[v]) >> i & 1)u = f[u][i];
if (u == v)return u;
for (int i = 19; i >= 0; i--)if (f[u][i] != f[v][i])u = f[u][i], v = f[v][i];
return f[u][0];
}
void dfs(int x, int y) {
f[x][0] = y; dfn[x] = ++CNT;
dep[x] = dep[y] + 1;
for (int j = 1; j <= 19; j++) {
f[x][j] = f[f[x][j - 1]][j - 1];
}
for (auto k : v[x])if (k != y) {
dfs(k, x);
}
}
void ins(int u) {
if(top==1){st[++top]=u;return;}
int lc=lca(u,st[top]);
if(dep[lc]==dep[st[top]]){st[++top]=u;return;}
while(top>1 && dep[lc]<=dep[st[top-1]]){
g[st[top-1]].pb(st[top]);
top--;
}
if(dep[lc]!=dep[st[top]]){
g[lc].pb(st[top]);
st[top]=lc;
}
st[++top]=u;
}
void in(vector<int>&da) {
sort(da.begin(), da.end(), [&](int x, int y) {return dfn[x] < dfn[y];});
top = 0; st[++top] = r;
for (auto k : da) {
if (k != 1)ins(k);
}
while (top > 1) {
g[st[top - 1]].pb(st[top]);
top--;
}
}
int dis(int x, int y) {
return dep[x] + dep[y] - 2 * dep[lca(x, y)];
}
} G;
int n,m;
LL cost[N];
vector<pair<int,LL>>v[N];
void _dfs(int x,int y,LL z){
cost[x]=z;
for(auto k:v[x]){
if(k.fi!=y){
_dfs(k.fi,x,min(z,k.se));
}
}
}
int vis[N];
LL dfs(int x,int y){
if(vis[x]) {
return cost[x];
}
LL res=0;
for(auto k:G.g[x]){
res+=min(1ll*cost[k],dfs(k,x));
}
return min(res,cost[x]);
}
void cl(int x){
for(auto k:G.g[x]){
cl(k);
}
G.g[x].clear();
}
int main() {
scanf("%d",&n);
G.init(n,1);
for(int i=1;i<n;i++){
int s,t;
LL w;
scanf("%d%d%lld",&s,&t,&w);
G.v[s].pb(t);
G.v[t].pb(s);
v[s].emplace_back(t,w);
v[t].emplace_back(s,w);
}
_dfs(1,0,1e15);
int q;
G.dfs(1,0);
for(scanf("%d",&q);q;q--){
int x,y;scanf("%d",&x);
vector<int>Query;
for(int i=1;i<=x;i++) {
scanf("%d",&y), Query.pb(y), vis[y] = 1;
// cout<<"in "<
}
G.in(Query);
printf("%lld\n",dfs(1,0));
for(auto k:Query)vis[k]=0;
cl(1);
}
return 0;
}
CF613D - Kingdom and its Cities
把虚树建出来直接dp。。。
#include
using namespace std;
typedef long long LL;
const int N = (1<<19)+5;
#define fi first
#define se second
#define pb push_back
#define wzh(x) cerr<<#x<<'='<
struct Virtual_tree {
vector<int>v[N], g[N];
int dep[N], f[N][22], st[N], top;
int n, r, dfn[N], CNT;
void init(int _n, int _r) {
n = _n; r = _r; CNT = 0;
for (int i = 1; i <= n; i++) {
v[i].clear();
g[i].clear();
}
}
int lca(int u, int v) {
if (dep[u] < dep[v])swap(u, v);
for (int i = 19; i >= 0; i--)if ((dep[u] - dep[v]) >> i & 1)u = f[u][i];
if (u == v)return u;
for (int i = 19; i >= 0; i--)if (f[u][i] != f[v][i])u = f[u][i], v = f[v][i];
return f[u][0];
}
void dfs(int x, int y) {
f[x][0] = y; dfn[x] = ++CNT;
dep[x] = dep[y] + 1;
for (int j = 1; j <= 19; j++) {
f[x][j] = f[f[x][j - 1]][j - 1];
}
for (auto k : v[x])if (k != y) {
dfs(k, x);
}
}
void ins(int u) {
if(top==1){st[++top]=u;return;}
int lc=lca(u,st[top]);
if(dep[lc]==dep[st[top]]){st[++top]=u;return;}
while(top>1 && dep[lc]<=dep[st[top-1]]){
g[st[top-1]].pb(st[top]);
top--;
}
if(lc!=st[top]){
g[lc].pb(st[top]);
st[top]=lc;
}
st[++top]=u;
}
void in(vector<int>&da) {
sort(da.begin(), da.end(), [&](int x, int y) {return dfn[x] < dfn[y];});
top = 0; st[++top] = r;
for (auto k : da) {
if(k!=1)ins(k);
}
while (top > 1) {
g[st[top - 1]].pb(st[top]);
top--;
}
}
int dis(int x,int y){
return dep[x]+dep[y]-2*dep[lca(x,y)];
}
} G;
int n,_q,vis[N],st=0,ans=0;
int sz[N];
void dfs(int x,int y){
sz[x]=vis[x];
for(auto k:G.g[x]){
if(k!=y){
dfs(k,x);
if(vis[x]&&vis[k]&&G.dis(x,k)==1)st=1;
if(vis[x]){
if(sz[k]){
sz[k]=0;
ans++;
}
}else{
sz[x]+=sz[k];
}
}
}
if(sz[x]>1)sz[x]=0,ans++;
G.g[x].clear();
}
int main() {
ios::sync_with_stdio(false);
cin>>n;G.init(n,1);
for(int i=1;i<n;i++){
int s,t;
cin>>s>>t;
G.v[s].pb(t);
G.v[t].pb(s);
}
G.dfs(1,0);
for(cin>>_q;_q;_q--){
int x;cin>>x;
vector<int>Query;st=0;
for(int i=1;i<=x;i++){
int y;
cin>>y;
vis[y]=1;
Query.pb(y);
}
G.in(Query);
ans=0;
dfs(1,0);
for(auto k:Query)vis[k]=0;
if(st)cout<<"-1\n";
else{
cout<<ans<<'\n';
}
}
return 0;
}
2020牛客暑期多校训练营(第一场)B Infinite Tree
因为树很大,所以不能把整个树求出来,意味着我们不知道每个点的具体dfs序,但是,显然由题可知相邻两个阶乘的dfs序必然是递增的,而且,树的第一层必然是1,第二层必然都是质数,从第一层到任意一个点的链上由上到下的质数必然是递减的。
那么我们先处理出每个阶乘节点的深度,深度显然是所有质因子幂次之和。就可以直接枚举每个阶乘。算两个相邻阶乘的lca的深度,相邻阶乘 i ! ( i + 1 ) ! i! ~~~(i+1)! i! (i+1)!的lca的深度就是(i+1)的maxdiv-n的质因子出现的次数。知道了这些东西之后就可以直接建虚树了,建完之后,先求出u=1的答案,然后往儿子上转移,如果转移能减小答案就转。记录最小值即可。
#include
using namespace std;
typedef long long LL;
const int N = 5e5 + 10;
#define fi first
#define se second
#define pb push_back
#define wzh(x) cerr<<#x<<'='<
int mindiv[N];
void init(){
mindiv[1]=1;
for(int i=2;i<N;i++){
if(!mindiv[i])mindiv[i]=i;
for(int j=i;j<N;j+=i){
if(!mindiv[j])mindiv[j]=i;
}
}
}
struct Virtual_tree {
vector<int>v[N], g[N];
int dep[N], st[N], top,lc[N];
int n, r,_t[N],tot;
void _ad(int x,int y){
for(;x<=n;x+=x&-x)_t[x]+=y;
}
int _su(int x){
int y=0;
for(;x;x-=x&-x)y+=_t[x];
return y;
}
void init(int _n, int _r) {
n = _n; r = _r;tot=n;top=0;
for (int i = 1; i <= n; i++) {
v[i].clear();
_t[i]=0;
}
dep[1]=lc[1]=1;
for(int i=2;i<=n;i++){
dep[i]=dep[i-1];
int j;
for(j=i;j!=mindiv[j];j/=mindiv[j]){
++dep[i];
}
lc[i]=_su(n)-_su(j-1)+1;//相邻节点lca的深度d
for(j=i;j!=1;j/=mindiv[j]){
_ad(mindiv[j],1);
}
dep[i]++;
}
}
void ins(int u) {
if (top == 1) {st[++top] = u; return ;}
if (lc[u] == dep[st[top]]) {st[++top] = u; return;}
while (top > 1 && lc[u] <= dep[st[top - 1]]) {
g[st[top - 1]].pb(st[top]);
top--;
}
if (lc[u] != st[top]) {
dep[++tot]=lc[u];
g[tot].pb(st[top]);
st[top] = tot;
}
st[++top] = u;
}
void in() {
top = 0; st[++top] = r;
for (int i=2;i<=n;i++) {
ins(i);
}
while (top > 1) {
g[st[top - 1]].pb(st[top]);
top--;
}
}
} G;
int n;
LL w[N],_su[N],ans;
void dfs(int x,int y){
_su[x]=w[x];
ans+=(G.dep[x]-G.dep[1])*w[x];
for(auto k:G.g[x]){
if(y!=k) {
dfs(k, x);
_su[x]+=_su[k];
}
}
}
void _dfs(int x,int y,LL tmp){
ans=min(ans,tmp);
for(auto k:G.g[x]){
if(k!=y){
if(_su[1]-_su[k]-_su[k]<0){
_dfs(k,x,tmp+1ll*(_su[1]-_su[k]-_su[k])*(G.dep[k]-G.dep[x]));
}
}
}
}
int main() {
ios::sync_with_stdio(false);
init();
while(cin>>n){
G.init(n,1);ans=0;
for(int i=1;i<=n;i++)cin>>w[i];
G.in();
dfs(1,0);
_dfs(1,0,ans);
cout<<ans<<'\n';
for(int i=1;i<=G.tot;i++){
w[i]=_su[i]=0;
G.g[i].clear();
}
}
return 0;
}