P6328 我是仙人掌 题解
一眼题。
首先处理出所有点之间的距离是简单的,可以用 BFS 在 $\mathcal{O}(n(n+m))$ 的时间内解决。
然后想一个相对暴力的做法,因为只要求至少一个二元组,那答案显然的等价于求出一个点集,使得每个点集中的点存在一个 $i$ 能够在 $y_i$ 步内到达 $x_i$。用一个类似染色的做法对所有 $x_i$ 做 $y_i$ 轮,显然就是被打了标记的点的数量。然后因为每个点至多只会被统计一次,每个 $x_i$ 互不干扰。容易想到使用 bitset 对每个 $x_i$ 能到达的点求交集。但是因为 $y_i$ 不一样,对每个点在预处理时用 $n$ 个 bitset 存储到它的距离 $\le i$ 的点集,这是 $\mathcal{O}(\frac{n^3}{w})$ 的。最后统计用一个 bitset 全局取并即可。
时间复杂度:$\mathcal{O}(\frac{n^3+n\sum a}{w})$。
空间复杂度:$\mathcal{O}(\frac{n^3}{w})$。
$20:43:00$ 我开始写。
$21:00:46$ 我写完了。配合光读喜提最优解。
Tips:接近完全图时 vector 明显快于链式前向星。
代码:
#define eb emplace_back
const int N=1e3+10;
int n,m,Q;
int dis[N];
bitset<N> b[N][N],vis,tmp;
vector<int> e[N];
int q[N],hd,tl,id[N];
void solve(int s){
vis.reset();
q[hd=tl=1]=s;dis[s]=0,vis[s]=1;b[s][0][s]=1;
while(hd<=tl){
int u=q[hd++];
for(int v:e[u])if(!vis[v]){
dis[v]=dis[u]+1;
b[s][dis[v]][v]=1;
vis[v]=1;
q[++tl]=v;
}
}
for(int i=1;i<=n;i++)b[s][i]|=b[s][i-1];
}
int main(){
n=read(),m=read(),Q=read();
for(int i=1,u,v;i<=m;i++)u=read(),v=read(),e[u].eb(v),e[v].eb(u);
for(int i=1;i<=n;i++)solve(i);
for(int qi=1;qi<=Q;qi++){
int num=read();
tmp.reset();
for(int i=1,x,y;i<=num;i++)x=read(),y=read(),tmp|=b[x][y];
we(tmp.count());
}
pcc();
return 0;
}