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

9 篇文章 0 订阅
4 篇文章 0 订阅

原题链接:https://www.lydsy.com/JudgeOnline/problem.php?id=5218

友好城市

Description

在Byteland 一共有n 座城市,编号依次为1 到n,这些城市之间通过m 条单向公路连接。对于两座不同的城市a 和b,如果a 能通过这些单向道路直接或间接到达b,且b 也能如此到达a,那么它们就会被认为是一对友好城市。Byteland 的交通系统十分特殊,第i 天只有编号在[li, ri] 的单向公路允许通行,请写一个程序,计算每天友好城市的对数。
注意:(a, b) 与(b, a) 没有区别。

Input

第一行包含三个正整数n, m, q,分别表示城市的个数、单向公路的条数以及询问的天数。
接下来m 行,每行两个正整数ui, vi,表示一条从城市ui 出发,通往城市vi 的单向道路。
接下来q 行,每行两个正整数li, ri,表示一个询问。
1 ≤ ui, vi ≤ n, ui != vi, 1 ≤ li ≤ ri ≤ m。N<=150,M<=300000,Q<=50000

Output

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

Sample Input

3 3 3
1 2
2 3
2 1
1 1
1 2
1 3

Sample Output

0
0
1

题解

HDU6072 Logical Chain实际上是一样的,加个莫队就能A,虽然正解是用分块+ST表维护边表。

代码

大家都加的莫队凭什么我被卡常???明明BZOJ过得贼快。

#include<bits/stdc++.h>
#define uint unsigned int
#define lg(x) __builtin_ctz(x)
using namespace std;
const int M=160,bit=31,N=3e5+5;
const uint inf=0xffffffff;
struct sd{int a,b,id;}ed[N],ask[N];
uint mmp[M][M/bit],mmp2[M][M/bit],vis[M/bit];
int cot[M],sta[M],ans[N],cnt[M][M],top,T,n,m,q,tot,siz;
bool operator<(sd x,sd y){return x.a/siz==y.a/siz?((x.a/siz&1)?x.b<y.b:x.b>y.b):x.a/siz<y.a/siz;}
void dfs1(int v){vis[v>>5]&=inf^(1<<(v&bit));for(int i=0;i<=(n>>5);++i)for(;mmp2[v][i]&vis[i];)dfs1(lg(mmp2[v][i]&vis[i])+(i<<5));sta[++top]=v;}
void dfs2(int v){vis[v>>5]&=inf^(1<<(v&bit)),++cot[tot];for(int i=0;i<=(n>>5);++i)for(;mmp[v][i]&vis[i];dfs2(lg(mmp[v][i]&vis[i])+(i<<5)));}
int work()
{
	int i,ans;memset(cot,tot=top=ans=0,sizeof(cot));
	for(memset(vis,255,sizeof(vis)),i=1;i<=n;++i)if(vis[i>>5]>>(i&bit)&1)dfs1(i);
	for(memset(vis,255,sizeof(vis)),i=top;i>=1;--i)if(vis[sta[i]>>5]>>(sta[i]&bit)&1)++tot,dfs2(sta[i]);
	for(int i=1;i<=tot;++i)ans+=cot[i]*(cot[i]-1)/2;
	return ans;
}
void add(int x){int a=ed[x].a,b=ed[x].b;if(!cnt[a][b]++)mmp[a][b>>5]|=1<<(b&bit),mmp2[b][a>>5]|=1<<(a&bit);}
void del(int x){int a=ed[x].a,b=ed[x].b;if(!--cnt[a][b])mmp[a][b>>5]&=inf^(1<<(b&bit)),mmp2[b][a>>5]&=inf^(1<<(a&bit));}
void in()
{
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=m;++i)scanf("%d%d",&ed[i].a,&ed[i].b);
	for(int i=1;i<=q;++i)scanf("%d%d",&ask[i].a,&ask[i].b),ask[i].id=i;
}
void ac()
{
	siz=sqrt(m);sort(ask+1,ask+1+q);
	int le=1,ri=1,i;
	for(add(1),i=1;i<=q;++i)
	{
		for(;le<ask[i].a;del(le++));
		for(;le>ask[i].a;add(--le));
		for(;ri<ask[i].b;add(++ri));
		for(;ri>ask[i].b;del(ri--));
		ans[ask[i].id]=work();
	}
	for(int i=1;i<=q;++i)printf("%d\n",ans[i]);
}
int main(){in(),ac();}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值