LgP9983 [USACO23DEC] Cowntact Tracing P
神。可惜自己实力不行。/ll
题意不说了。难点其实是想到一个合理的贪心方法。才能进一步优化。
考虑全部为 \(1\) 的,容易想到的贪心策略是找到一个深度最大的且未被覆盖的点,找到这个点的 \(k\) 级祖先 \(x\),将 \(x\) 设为初始点,知道所有点都被覆盖。
考虑有 \(0\) 的情况。记录 \(td_u\) 表示每个点到未感染点的最小距离。显然只有 \(td_u>k\) 的点 \(u\) 才有可能作为初始点。考虑类似上面的贪心过程。每次找到一个深度最大的且未被覆盖的感染点。找到一个能作为初始点的深度最小的点 \(x\),使得 \(x\) 能覆盖 \(u\),令 \(x\) 为初始点。如此,知道所有感染点都被覆盖。直接暴力做可以做到 \(O(n^2Q)\)。这其实是很自然的思考过程。证明显然。
考虑优化这个贪心即可。假如我们每次能找到 \(x\) 的话,只需要按深度从大到小枚举 \(u\),判断 \(u\) 是否被覆盖即可。考虑点分树,记 \(lw_u\) 表示u的点分树种到点 \(u\) 的最短距离,查询和修改每次枚举\(u\) 在点分树上祖先即可。复杂度 \(O(\log n)-O(\log n)\)。
问题在于已知 \(u\),如何找到 \(x\)。考虑 \(p=lca(u,x)\),因为 \(u\) 是深度最大的点,显然在这之后 \(p\) 的子树都被覆盖,那么贪心的,我们希望 \(p\) 深度越小越好。那么我们记录 \(pv_u\) 表示里 \(u\) 最近的可初始点。相当于 \(dep_u-dep_p + dis(p,pv_p)\le k\)。记录 \(b_x=dep_x+k-dis(x,pv_x)\)。相当于 \(b_x\le dep_u\),显然 \(b_{fa_x}\le b_x\),因为可以让 \(fa_x\)选的点和 \(x\) 选的点相同。倍增找到深度最小的 \(p\) 即可。
点击查看代码
#include<bits/stdc++.h>
#define Pr pair<int,int>
#define fi first
#define nd second
using namespace std;
typedef long long LL;
inline int read(){
char ch=getchar();
while(!isdigit(ch) && ch!='-') ch=getchar();
int x=0,ff=1; if(ch=='-') ff=-1,ch=getchar();
while(isdigit(ch)) x=(x<<3) + (x<<1) + (ch^48),ch=getchar();
return x*ff;
}
const int N=1e5+5,inf=1e9;
int n,Q,D,va[N],td[N],rT,smn,siz[N],p1[N],dep[N],fa[N][20],ld[N],pv[N],lmd[N]; bool vis[N]; char ch2[N]; vector<int> e[N]; vector<Pr> vf[N];
void findrt(int u,int Fa,int S){ if(!Fa) rT=0,smn=inf;
siz[u]=1; int mx=0;
for(int v:e[u]) if(v!=Fa && !vis[v]) findrt(v,u,S), siz[u]+=siz[v],mx=max(mx,siz[v]);
mx=max(mx,S-siz[u]); if(mx<smn) smn=mx,rT=u;
}
void dfsd(int u,int Fa,int d){
vf[u].push_back({rT,d});
for(int v:e[u]) if(v!=Fa && !vis[v]) dfsd(v,u,d+1);
}
void dfz(int u){
dfsd(u,0,0); vis[u]=1; findrt(u,0,0);
for(int v:e[u]) if(!vis[v]) findrt(v,0,siz[v]),dfz(rT);
}
void dfs1(int u,int Fa){
dep[u]=dep[Fa]+1; fa[u][0]=Fa;
for(int i=1;i<=18;i++) fa[u][i]=fa[fa[u][i-1]][i-1];
for(int v:e[u]) if(v!=Fa) dfs1(v,u);
}
bool chk(int u){ if(!va[u]) return 1;
for(auto [x,d]:vf[u]) if(lmd[x]+d<=D) return 1;
return 0;
}
void addw(int u){
for(auto [x,d]:vf[u]) lmd[x]=min(lmd[x],d);
}
void vmain(){
D=read(); for(int i=1;i<=n;i++) ld[i]=lmd[i]=inf,pv[i]=0;
queue<int> q; for(int i=1;i<=n;i++) if(td[i]>D) pv[i]=i,ld[i]=0,q.push(i);
while(!q.empty()){
int u=q.front(); q.pop();
for(int v:e[u]) if(!pv[v]) pv[v]=pv[u],ld[v]=ld[u]+1,q.push(v);
}
for(int i=1;i<=n;i++) ld[i]=dep[i]+D-ld[i];
int cnt=0;
for(int ii=1;ii<=n;ii++) { int u=p1[ii]; if(chk(u)) continue;
if(ld[u]<dep[u]) {puts("-1"); return ;}
int x=u; for(int i=18;i>=0;i--) if(fa[x][i] && ld[fa[x][i]]>=dep[u]) x=fa[x][i];
x=pv[x]; addw(x); cnt++;
} printf("%d\n",cnt);
}
int main(){
n=read(); scanf("%s",ch2+1); for(int i=1;i<=n;i++) va[i]=ch2[i]-'0';
for(int i=1;i<n;i++){
int u=read(),v=read();
e[u].push_back(v); e[v].push_back(u);
} dfs1(1,0);
for(int i=1;i<=n;i++) td[i]=inf;
queue<int> q; for(int i=1;i<=n;i++) if(!va[i]) td[i]=0,q.push(i);
while(!q.empty()){
int u=q.front(); q.pop();
for(int v:e[u]) if(td[v]==inf) td[v]=td[u]+1,q.push(v);
} findrt(1,0,n); dfz(rT);
for(int i=1;i<=n;i++) p1[i]=i;
sort(p1+1,p1+n+1,[&](int x,int y){return dep[x]>dep[y];});
Q=read(); while(Q--) vmain();
return 0;
}

浙公网安备 33010602011771号