给定一张 n n n个点 m m m条边的DAG,保证所有边都是从编号小的点往编号大的点连,给 q q q次询问,每次询问给出一个点和一个点集,点集大小为 k k k,询问对于可达这个点的除该点集外的最大距离是多少。
( n ≤ 1 e 5 , m ≤ 2 e 5 , q ≤ 1 e 5 , ∑ k ≤ 1 e 5 ) (n\le 1e5,m\le 2e5,q\le1e5,\sum k\le 1e5) (n≤1e5,m≤2e5,q≤1e5,∑k≤1e5)
一开始我口胡了线段树合并的算法,写完之后发现假掉了,然后就自闭了好久。后来我发现,这题的 ∑ k \sum k ∑k最大只有 1 e 5 1e5 1e5,所以他如果每次询问的点集大小很大的话,那询问次数就很少,如果每次询问的点集大小很小的话,那询问次数才会到达 1 e 5 1e5 1e5级别。于是我们可以自然而然想到这么一个算法,设 B = n B=\sqrt n B=n,然后我们对于每个点,预处理出可到达它的所有点的距离从大到小排序的前 B B B个点,由于边数只有 2 e 5 2e5 2e5级别,那么我们每次可以对于每条边转移时暴力 O ( B ) O(B) O(B)转移,来得到所有点的前 B B B大距离。那么如果 k < B k<B k<B的话,说明他除去的点不足以把这 B B B个点都去除,所以我们可以暴力从大到小找到第一个没有被删去的点,这个点的距离就是答案。如果 k ≥ B k\ge B k≥B的话,说明这样的询问次数最多只有 1 e 5 / B 1e5/B 1e5/B次,那么我们可以直接暴力跑图找答案。这样复杂度均摊就是 q ∗ B = q n q*B=q\sqrt n q∗B=qn的,足以通过此题。
#pragma GCC optimize(3,"Ofast","inline")
#include
using namespace std;
typedef long long ll;
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
/*================Header Template==============*/
const int maxn=100005,maxs=400;
int n,m,q,cnt[maxn],fx[maxn][maxs][2],clk[maxn],f[maxn],fxx,fy[maxs][2];
vector<int>G[maxn];
int main() {
read(n),read(m),read(q);
for(int i=0,u,v;i<m;++i)
read(u),read(v),G[v].push_back(u);
for(int u=1;u<=n;++u) {
cnt[u]=1,fx[u][0][0]=0,fx[u][0][1]=u;
for(auto v:G[u]) {
// cerr<<"Edge "< "<
++fxx;
int l=0,r=0,tot=0;
for(;l<cnt[u]&&r<cnt[v]&&tot<maxs;) {
while(l<cnt[u]&&clk[fx[u][l][1]]==fxx)
++l;
while(r<cnt[v]&&clk[fx[v][r][1]]==fxx)
++r;
if(l>=cnt[u]||r>=cnt[v])
break;
// cerr<<"Now == : l="<
if(fx[u][l][0]>fx[v][r][0])
fy[tot][0]=fx[u][l][0],
fy[tot][1]=fx[u][l][1],
clk[fx[u][l][1]]=fxx,
++l,
++tot;
else
fy[tot][0]=fx[v][r][0]+1,
fy[tot][1]=fx[v][r][1],
clk[fx[v][r][1]]=fxx,
++r,
++tot;
}
// cerr<
for(;l<cnt[u]&&tot<maxs;++l)
if(clk[fx[u][l][1]]!=fxx)
fy[tot][0]=fx[u][l][0],
fy[tot][1]=fx[u][l][1],
clk[fx[u][l][1]]=fxx,
++tot;
for(;r<cnt[v]&&tot<maxs;++r)
if(clk[fx[v][r][1]]!=fxx)
fy[tot][0]=fx[v][r][0]+1,
fy[tot][1]=fx[v][r][1],
clk[fx[v][r][1]]=fxx,
++tot;
// cerr<
cnt[u]=tot;
for(int i=0;i<tot;++i)
fx[u][i][0]=fy[i][0],fx[u][i][1]=fy[i][1];
}
}
// cerr<<"Now =========================== "<
// for(int i=1;i<=n;++i) {
// printf("Index = %d, Count %d:\n",i,cnt[i]);
// for(int j=0;j
// printf("Des = %d, Dis = %d\n",fx[i][j][1],fx[i][j][0]);
// }
for(int i=0,t,y;i<q;++i) {
++fxx;
read(t),read(y);
for(int j=0,x;j<y;++j)
read(x),clk[x]=fxx;
int ans=-1;
if(y<=maxs) {
for(int j=0;j<cnt[t];++j)
if(clk[fx[t][j][1]]!=fxx) {
ans=fx[t][j][0];
break;
}
}
else {
for(int j=1;j<=t;++j) {
f[j]=clk[j]==fxx?-1e9:0;
for(auto v:G[j])
f[j]=max(f[j],f[v]+1);
}
ans=max(ans,f[t]);
}
printf("%d\n",ans);
}
}