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
",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
",dp[x]);  
		}
	}
	return 0;  
}

  

原文地址:https://www.cnblogs.com/guangheli/p/12411856.html