JZOJ5932. 【NOIP2018模拟10.27】情报中心

题目

最近,C国成功地渗透进入了D国的一个城市。这个城市可以抽象成一张有n(n≤1000)个节点,节点之间有m条双向道路连接的无向图,每条道路的⻓度都为1。 经过侦查,C国情报部部⻓GGB惊讶地发现,这座看起来不起眼的城市竟然是D国的军事 中心。因此GGB决定在这个城市内设立情报机构。情报专家TAC在侦查后,安排了q 种设立 情报机构的方案。这些方案中,第i种方案将计划建立ki 个情报机构,第j个情报机构可以安排 人员到距离其不超过di,j 的节点上收集情报。 但是,由于人手不足,GGB只能安排上述q种方案中的一种进行实施。为了评估一种方案的 性能,我们把能够收集到情报的节点数量视为这种情报的价值。现在,小∞ ⇔被GGB和TAC派 来侦查,请你帮他统计每一种方案的价值。

题解

一种最朴素的暴力做法,
每次询问都向外扩散,统计有多少个点。
不过可以发现,在这个过程之中,是有非常多重复的操作的,
这样大大减少了时间效率。
考虑如何存下来,
f i , k , x f{i,k,x} fi,k,x表示从第i个点出发,向外走k步,能否到达点x。
首先先预处理f数组,
然后每次询问的时候调用。
分析一下复杂度,预处理f数组要先枚举一个起点,然后遍历完所有的边于是这一部分就是O(nm)的。
对于询问,就是输入的每个点都有枚举一下所有的点,看看是否能够走到,这一部分就是O(n ∑ \sum k)的。
其实,f数组可以bitset实现的,这样又可以除上一个32。

code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <bitset> 
#define N 1003
#define P putchar
#define G getchar
using namespace std;
char ch;
void read(int &n)
{
	n=0;
	ch=G();
	while((ch<'0' || ch>'9') && ch!='-')ch=G();
	int w=1;
	if(ch=='-')w=-1,ch=G();
	while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
	n*=w;
}

void write(int x){if(x>9) write(x/10);P(x%10+'0');}

int n,m,q,x,y,tot,a[N][N],l[N];
int d[N],head,tail,k,dis[N];
bool bz[N][N];

bitset<N> ans,f[N][N];

void work(int st)
{
	memset(dis,127,sizeof(dis));
	for(dis[d[tail=1]=st]=0,head=0;head<tail;)
	{
		x=d[++head];y=dis[x];
		for(int i=1;i<=l[x];i++)
			if(dis[a[x][i]]>n)dis[d[++tail]=a[x][i]]=y+1;
	}
	for(int i=1;i<=n;i++)if(dis[i]<n)f[st][dis[i]][i]=1;
	for(int i=1;i<=n;i++)f[st][i]=f[st][i]|f[st][i-1];
}

int main()
{
	freopen("center.in","r",stdin);
	freopen("center.out","w",stdout);
	
	read(n);read(m);read(q);
	for(int i=1;i<=m;i++)
		read(x),read(y),bz[x][y]=bz[y][x]=1;
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(bz[i][j])a[i][++l[i]]=j;
	for(int i=1;i<=n;i++)work(i);
	for(int i=1;i<=q;i++)
	{
		read(k);ans.reset();
		for(int i=1;i<=k;i++)read(x),read(y),ans=ans|f[x][y];
		write(ans.count());P('\n');
	}
	
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值