LOJ #2838. 「JOISC 2018 Day 3」比太郎的聚会 根号分治
这道题的数据范围中有两个需要注意到的点:
1. 边都是由编号小的点连向编号大的点.
2. 总点数只有 $10^5$ 个.
所以我们可以考虑采取根号分治的做法:
对于点数大于 $\sqrt n$ 的部分,直接跑一个 $O(n)$ 的 DP.
对于点数小于 $\sqrt n$ 的部分,提前预处理.
这样,时间复杂度就是 $O(n \sqrt n)$ 的.
code:
#include <bits/stdc++.h>
#define B 150
#define N 100060
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
int n,m,Q;
int vis[N],dp[N];
priority_queue<pair<int,int> >q[N];
pair<int,int>d[N][B+3];
vector<int>G[N];
int main()
{
// setIO("input");
int i,j,k;
scanf("%d%d%d",&n,&m,&Q);
for(i=1;i<=m;++i)
{
int x,y;
scanf("%d%d",&x,&y);
G[x].push_back(y);
}
for(i=1;i<=n;++i)
{
for(j=1;j<=B;++j)
d[i][j]=make_pair(-1,0);
q[i].push(make_pair(0,i));
for(j=1;j<=B;++j)
{
while(!q[i].empty()&&vis[q[i].top().second]==i)
q[i].pop();
if(q[i].empty())
break;
d[i][j]=q[i].top();
vis[q[i].top().second]=i;
q[i].pop();
}
for(j=0;j<G[i].size();++j)
for(k=1;k<=B&&d[i][k].second;++k)
q[G[i][j]].push(make_pair(d[i][k].first+1,d[i][k].second));
}
memset(vis,0,sizeof(vis));
for(i=1;i<=Q;++i)
{
int x,y,z;
scanf("%d%d",&x,&y);
for(j=1;j<=y;++j)
scanf("%d",&z),vis[z]=i;
if(y<B)
{
for(j=1;j<=B;++j)
if(vis[d[x][j].second]!=i)
{
printf("%d\n",d[x][j].first);
break;
}
}
else
{
memset(dp,-1,sizeof(dp));
for(j=1;j<=n;++j)
{
if(vis[j]!=i)
dp[j]=max(dp[j],0);
if(dp[j]>=0)
{
for(k=0;k<G[j].size();++k)
dp[G[j][k]]=max(dp[G[j][k]],dp[j]+1);
}
}
printf("%d\n",dp[x]);
}
}
return 0;
}

浙公网安备 33010602011771号