[BZOJ 5218] [Lydsy2017省队十连测]友好城市

7 篇文章 0 订阅
7 篇文章 0 订阅
BZOJ传送门

题目描述

在 Byteland 一共有 n n n 座城市,编号依次为 1 1 1 n n n,这些城市之间通过 m m m 条单向公路连接。

对于两座不同的城市 a a a b b b,如果 a a a 能通过这些单向道路直接或间接到达 b b b,且 b b b 也能如此到达 a a a, 那么它们就会被认为是一对友好城市。 Byteland 的交通系统十分特殊,第 i i i 天只有编号在 [ l i , r i ] [l_i,r_i] [li,ri] 的单向公路允许通行,请写一个程序,计算每天友好城市的对数。

注意: ( a , b ) (a,b) (a,b) ( b , a ) (b,a) (b,a) 没有区别。

输入输出格式

输入格式

第一行包含三个正整数 n , m , q n,m,q n,m,q,分别表示城市的个数、单向公路的条数以及询问的天数。

接下来 m m m 行,每行两个正整数 u i , v i u_i,v_i ui,vi,表示一条从城市 u i u_i ui 出发,通往城市 v i v_i vi 的单向道路。

接下来 q q q 行,每行两个正整数 l i , r i l_i,r_i li,ri,表示一个询问。

输出格式

输出 q q q 行,每行一个整数,即友好城市的对数。

输入输出样例

输入样例#1:
3 3 3
1 2
2 3
2 1
1 1
1 2
1 3
输出样例#1:
0
0
1

数据范围

对于 100% 的数据, 1 ≤ u i , v i ≤ n , u i ≠ v i , 1 ≤ l i ≤ r i ≤ m 1≤u_i,v_i≤n,u_i≠v_i,1≤l_i≤r_i≤m 1ui,vin,ui̸=vi,1lirim

img

解题分析

这道题的套路, 套个莫队就好了。

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cctype>
#include <cstdlib>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define uint unsigned int
#define MX 200
#define ME 300050
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
struct Bitset
{
	uint dat[5];
	IN void clear() {std::memset(dat, 0, sizeof(dat));}
	IN void set(R int x) {dat[x >> 5] |= (1u << (x & 31));}
	IN void rev(R int x) {dat[x >> 5] ^= (1u << (x & 31));}
	IN bool chk(R int x) {return dat[x >> 5] & (1u << (x & 31));}
}vis, G[MX], RG[MX];
struct Edge {int from, to;} edge[ME];
struct Eve {int lef, rig, id;} eve[ME];
int sta[MX], seg[ME], ans[ME], cnt[MX][MX];
IN bool operator < (const Eve &x, const Eve &y)
{
	if(seg[x.lef] == seg[y.lef])
	{
		if(seg[x.lef] & 1) return seg[x.rig] < seg[y.rig];
		else return seg[x.rig] > seg[y.rig];
	}
	return seg[x.lef] < seg[y.lef];
}
IN void add(R int from, R int to)
{
	cnt[from][to]++;
	if(cnt[from][to] == 1) G[from].set(to), RG[to].set(from);
}
IN void out(R int from, R int to)
{
	cnt[from][to]--;
	if(!cnt[from][to]) G[from].rev(to), RG[to].rev(from);
}
int dot, line, q, top, ct;
void DFS1(R int now)
{
	vis.rev(now); R uint stat;
	for (R int i = 0; i < 5; ++i)
	{
		W (233)
		{
			stat = RG[now].dat[i] & vis.dat[i];
			if(!stat) break;
			DFS1(i << 5 | __builtin_ctz(stat));
		}
	}
	sta[++top] = now;
}
void DFS2(R int now)
{
	vis.rev(now); R uint stat; ++ct;
	for (R int i = 0; i < 5; ++i)
	{
		W (233)
		{
			stat = G[now].dat[i] & vis.dat[i];
			if(!stat) break;
			DFS2(i << 5 | __builtin_ctz(stat));
		}
	}
}
IN int solve()
{
	int ret = top = 0;
	for (R int i = 0; i < dot; ++i) vis.set(i);
	for (R int i = 0; i < dot; ++i) if(vis.chk(i)) DFS1(i);
	for (R int i = 0; i < dot; ++i) vis.set(i);
	for (R int i = dot; i; --i) if(vis.chk(sta[i]))
	{
		ct = 0; DFS2(sta[i]);
		ret += ct * (ct - 1) / 2;
	}
	return ret;
}
int main(void)
{
	R int l, r;
	in(dot), in(line), in(q);
	int bd = std::sqrt(line) + 1;
	for (R int i = 1; i <= line; ++i) seg[i] = (i - 1) / bd + 1; 
	for (R int i = 1; i <= line; ++i) in(edge[i].from), in(edge[i].to), edge[i].from--, edge[i].to--;
	for (R int i = 1; i <= q; ++i) in(eve[i].lef), in(eve[i].rig), eve[i].id = i;
	std::sort(eve + 1, eve + 1 + q); l = eve[1].lef, r = eve[1].rig;
	for (R int i = eve[1].lef; i <= eve[1].rig; ++i) add(edge[i].from, edge[i].to);
	ans[eve[1].id] = solve();
	for (R int i = 2; i <= q; ++i)
	{
		W (l < eve[i].lef) out(edge[l].from, edge[l].to), ++l;
		W (l > eve[i].lef) --l, add(edge[l].from, edge[l].to);
		W (r < eve[i].rig) ++r, add(edge[r].from, edge[r].to);
		W (r > eve[i].rig) out(edge[r].from, edge[r].to), --r;
		ans[eve[i].id] = solve();
	}
	for (R int i = 1; i <= q; ++i) printf("%d\n", ans[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值