P4185 [USACO18JAN] MooTube G 题解
P6111 [USACO18JAN] MooTube S 题解(数据弱化版)
前言
如标题所言,是双倍经验。不同的是P6111可以用DFS暴力过去,但是P4185不行。本篇重点讲的是非暴力做法。
思路
这道题显然不需要在线查询。所以我们可以考虑离线。每次查询每条边的边权显然是T飞了,因此我们可以考虑给查询的数组拍一下序,然后每次加边。
因此我们可以将询问按 \(k,w\) 的值降序排序后离线解决本题。
做法过程:
- 把边按照 \(w\) 从大到小排序。
- 把查询按照 \(k\) 从大到小排序。
- 对于每次查询的 \(k\),我们可以把所有边权 \(w \geq k\) 的边加入一个并查集,用并查集维护当前可以推荐的视频。
- 满足条件的推荐视频数量即为 \(size_v-1\)。(因为不能包含 \(v\) 本身)
代码
const int N=1e5+10;
int n,Q;
struct node{
int u,v,w;
}e[N];//读入的边
bool cmpw(node A,node B){return A.w>B.w;}
struct qry{
int k,v,id;
int ans;
}a[N];//读入的询问
bool cmpk(qry A,qry B){return A.k>B.k;}
bool cmpid(qry A,qry B){return A.id<B.id;}
int fa[N],siz[N];
int findf(int x){//并查集查找
if(fa[x]==x) return x;
return fa[x]=findf(fa[x]);
}
void merge(int x,int y){//并查集合并
int fx=findf(x),fy=findf(y);
if(fx!=fy){
fa[fx]=fy;
siz[fy]+=siz[fx];//这里一定要搞清谁合并到谁里面了,不要搞反了!
return ;
}
}
signed main(){
n=Read();Q=Read();
for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1;//初始化,不要忘了!
for(int i=1;i<n;i++){
e[i].u=Read();e[i].v=Read();e[i].w=Read();
}
sort(e+1,e+n,cmpw);
for(int i=1;i<=Q;i++){
a[i].k=Read();a[i].v=Read();
a[i].id=i;
}
sort(a+1,a+Q+1,cmpk);
int j=1;//当前加到第 j 个边
for(int i=1;i<=Q;i++){
while(j<n&&e[j].w>=a[i].k){
merge(e[j].u,e[j].v);
j++;
}
a[i].ans=siz[findf(a[i].v)]-1;
}
sort(a+1,a+Q+1,cmpid);
for(int i=1;i<=Q;i++){
printf("%d\n",a[i].ans);
}
return 0;
}
浙公网安备 33010602011771号