【題解】Luogu P5068 [Ynoi2015]我回來了
阿新 • • 發佈:2018-12-15
眾所周知lxl是個毒瘤,Ynoi道道都是神仙題,這道題極其良心,題面好評
原題傳送門
我們先珂以在\(O(n^2)\)的時間內bfs求出任意兩點距離
我們考慮如何計算從一個點到所有點的最短路長度小於等於k的點的數量
我們先求出來從一個點到所有點的最短路長度等於k的點的數量,這個珂以在bfs搜尋過程中完成
統計最短路長度小於等於k的點的數量珂以使用字首和
這裡明顯不好直接字首和,我們可以使用bitset來維護一個點到所有點的最短路長度小於等於k的點的數量,如果一位是1,就代表滿足條件,否則不滿足條件
做字首和時就直接用“|”就行了、
以上搜索和字首和的複雜度為\(O(\frac {n^3}{\omega})\) (\(\omega\)是一個常數,這裡珂以取32)
最後詢問時開一個bitset,把所有條件的答案“|”起來,就是求出滿足條件點的個數,最後輸出這個bitset的count(),也就是1(滿足)的個數
查詢的複雜度為\(O(\frac {n \sum_{i=1}^{q} a}{\omega})\)
這道題存邊不能用鏈式前向星,鏈式前向星會tle,因為地址不是連續的,所以用vector存邊就行了qaq
總複雜度為\(O(\frac {n^3+n \sum_{i=1}^{q} a}{\omega})\)
#include <bits/stdc++.h> #define N 1010 #define M 100010 #define getchar nc using namespace std; inline char nc(){ static char buf[100000],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; } inline int read() { register int x=0,f=1;register char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return x*f; } inline void write(register int x) { if(!x)putchar('0');if(x<0)x=-x,putchar('-'); static int sta[20];register int tot=0; while(x)sta[tot++]=x%10,x/=10; while(tot)putchar(sta[--tot]+48); } vector <int> to[N]; int n,m,q; int dis[N][N]; bitset <N> dissum[N][N]; inline void bfs(register int x,register int id) { for(register int i=1;i<=n;++i) dis[id][i]=1005; queue <int> q; q.push(x); dis[id][x]=0; while(!q.empty()) { int u=q.front(); q.pop(); for(register int i=0;i<to[u].size();++i) if(dis[id][to[u][i]]==1005) { dis[id][to[u][i]]=dis[id][u]+1; q.push(to[u][i]); } } for(register int i=1;i<=n;++i) dissum[id][dis[id][i]].set(i); for(register int i=1;i<=n;++i) dissum[id][i]|=dissum[id][i-1]; } int main() { n=read(),m=read(),q=read(); while(m--) { int u=read(),v=read(); to[u].push_back(v),to[v].push_back(u); } for(register int i=1;i<=n;++i) bfs(i,i); while(q--) { bitset <N> ans; int x=read(); while(x--) { int u=read(),k=read(); ans|=dissum[u][k]; } write(ans.count()),puts(""); } return 0; }