Loading

题解-CF348E Pilgrims

题面

CF348E Pilgrims

有一棵 \(n\) 个点的 带权 树和 \(m\) 个关键点,要求杀了一个不关键的点,满足最多的关键点到离它最远的所有关键点的路径都被打断。求可以满足的最多关键点数和此时的杀点方案数。

数据范围:\(2\le m<n\le 10^5\)


题解

写了个奇怪的做法调了一个上午,写篇不行的题解。

很明显所有关键点到所有离它最远的关键的所有路径都经过一个点:

距离最远的一对关键点的中点(不需要取 \(\frac{dist}{2}\) 的位置建出一个新点!如果真正的中心在边上,边上的两点都行!)。

对于每个点 \(u\),如果以它为根,离它最远的所有点的 \(\rm lca\)\(v\),只有杀 \(u\)\(v\) 路径上的点才能杀死 \(u\)

所以目标就是对于每个 \(u\)\(v\),然后最后树上差分就可以了。

可以以上文中的中点为根,边 dfs 边找,这个 \(v\) 是可以动态维护的。

如果当前 dfs 到节点 \(u\),这个 \(v\) 绝不会是它的子树点,甚至不可能是和 \(u\) 在根的同一棵子树内的点。

所以所有离 \(u\) 最远的点的当前 \(\rm lca\) 就是 \(v\)

可以线段树,维护区间最大值和最大值点的 \(\rm lca\),支持区间加减。

然后就解决了。如果嫌弃 \(\Theta(\log n)\)pushup,可以用 ST\(\Theta(1)\) lca


代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define x first
#define y second
#define bg begin()
#define ed end()
#define pb push_back
#define mp make_pair
#define sz(a) int((a).size())
#define R(i,n) for(int i(0);i<(n);++i)
#define L(i,n) for(int i((n)-1);~i;--i)
const int iinf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;

//Data
const int N=1e5+1;
int n,m,rt=-1,mon[N];
bool imon[N],newed;

//Tree_0
const int D=20;
vector<int> e[N],to,we;
void adde(int u,int v,int w){
    e[u].pb(sz(to)),to.pb(v),we.pb(w);
    e[v].pb(sz(to)),to.pb(u),we.pb(w);
}
int dep[N],pre[N],in,dfn[N],lim[N],no[N];
int in2,dfn2[N],lg[N<<1]; pair<int,int> st[D][N<<1];
void dfs0(int u){
    for(int v:e[u])if(to[v]^to[pre[u]])
        dep[to[v]]=dep[u]+we[v],pre[to[v]]=v^1,dfs0(to[v]);
}
void dfs1(int u){
    no[dfn[u]=in++]=u,st[0][dfn2[u]=in2++]=mp(dep[u],u);
    for(int v:e[u])if(to[v]^pre[u])
        dep[to[v]]=dep[pre[to[v]]=u]+we[v],dfs1(to[v]),
            st[0][in2++]=mp(dep[u],u);
    lim[u]=in;
}
void linit(){
    R(i,D-1)R(u,in2-(2<<i)+1)
        st[i+1][u]=min(st[i][u],st[i][u+(1<<i)]);
    R(i,in2+1)if(i>1) lg[i]=lg[i>>1]+1;
}
int lca(int u,int v){
    u=dfn2[u],v=dfn2[v];
    if(u>v) swap(u,v); v++; int i=lg[v-u];
    return min(st[i][u],st[i][v-(1<<i)]).y;
}

//SegmentTree
const int sN=N<<2;
#define mid ((l+r)>>1)
int mx[sN],tag[sN],anc[sN];
void pushup(int u){
    if(mx[u*2+1]>mx[u*2+2]) mx[u]=mx[u*2+1],anc[u]=anc[u*2+1];
    else if(mx[u*2+2]>mx[u*2+1]) mx[u]=mx[u*2+2],anc[u]=anc[u*2+2];
    else mx[u]=mx[u*2+1],anc[u]=lca(anc[u*2+1],anc[u*2+2]);
}
void pushtag(int u,int v){tag[u]+=v,mx[u]+=v;}
void pushdown(int u){tag[u]&&(pushtag(u*2+1,tag[u]),
    pushtag(u*2+2,tag[u]),tag[u]=0);}
void sinit(int u=0,int l=0,int r=n){
    if(r-l==1) return mx[u]=imon[no[l]]?dep[no[l]]:-iinf,anc[u]=no[l],void();
    sinit(u*2+1,l,mid),sinit(u*2+2,mid,r),pushup(u);
}
void fixr(int x,int y,int v,int u=0,int l=0,int r=n){
    if(r<=x||y<=l) return; if(x<=l&&r<=y) return pushtag(u,v);
    pushdown(u),fixr(x,y,v,u*2+1,l,mid),fixr(x,y,v,u*2+2,mid,r),pushup(u);
}
void print(int u=0,int l=0,int r=n){
    if(r-l==1) return void(cout<<"["<<l<<"]:"<<mx[u]<<" ");
    pushdown(u),print(u*2+1,l,mid),print(u*2+2,mid,r);
}
#undef mid

//Tree_1
int mk[N];
void dfs2(int u){
    if(imon[u]) mk[u]++,mk[anc[0]]++,mk[rt]--;
    for(int v:e[u])if(to[v]^pre[u]){
        fixr(0,n,we[v]),fixr(dfn[to[v]],lim[to[v]],-2*we[v]);
        dfs2(to[v]),fixr(0,n,-we[v]),fixr(dfn[to[v]],lim[to[v]],2*we[v]);
    }
}
void dfs3(int u){
    for(int v:e[u])if(to[v]^pre[u]) dfs3(to[v]),mk[u]+=mk[to[v]];
}

//Main
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n>>m;
    R(i,m) cin>>mon[i],--mon[i],imon[mon[i]]=true;
    R(i,n-1){int u,v,w; cin>>u>>v>>w,--u,--v,adde(u,v,w);}
    R(u,n) sort(e[u].bg,e[u].ed);
    int s=-1,t=-1,now=0;
    R(u,n) pre[u]=-1; dep[mon[0]]=0,dfs0(mon[0]);
    R(i,m)if(!~s||dep[mon[i]]>dep[s]) s=mon[i];
    R(u,n) pre[u]=-1; dep[s]=0,dfs0(s);
    R(i,m)if(!~t||dep[mon[i]]>dep[t]) t=mon[i];
    for(int u=t;u^s;u=to[pre[u]]){
        if(now>=dep[t]/2){rt=u;break;}
        now+=we[pre[u]];
    }
    if(!~rt) rt=s;
    R(u,n) pre[u]=-1;
    dep[rt]=0,dfs1(rt),linit(),sinit(),dfs2(rt),dfs3(rt);
    int mx=-1,cnt=0;
    R(u,n)if(!imon[u]){
        if(mk[u]>mx) mx=mk[u],cnt=1;
        else if(mk[u]==mx) cnt++; 
    }
    cout<<mx<<" "<<cnt<<'\n';
    return 0;
}

祝大家学习愉快!

posted @ 2020-11-25 11:47  George1123  阅读(150)  评论(1编辑  收藏  举报