jzoj4240 [五校联考5day2]游行 拓扑排序+倍增lca+线段树优化建图

17 篇文章 0 订阅

Description


恶梦是学校里面的学生会主席。他今天非常的兴奋,因为学校一年一度的学生节开始啦!!
在这次节日上总共有N个节目,并且总共也有N个舞台供大家表演。其中第i个节目的表演时间为第i个单位时间,表演的舞台为Ai,注意可能有多个节目使用同一个舞台。
作为恶梦的忠实粉丝之一的肥佬,当然要来逛一下啦,顺便看一下能不能要到恶梦的签名。
肥佬一开始会先在A1 看完节目1再去闲逛。
肥佬可以在舞台之间随便乱走。但是假如肥佬当前在看第i个节目,站在第Ai个舞台前面的话,由于有些道路被封锁了,所以肥佬下一步只能前往第Li到第Ri个舞台中的一个。并且当一个节目结束的时候,肥佬只能去看另外一个节目,或者结束自己的闲逛。
具体而言就是说,假设肥佬可以从第i个节目走去第j个节目,那么当且仅当i<j且Li <= Aj <= Ri。
但事实上是,恶梦非常讨厌被自己的粉丝跟踪。所以他想在只封锁掉一个节目的情况下,使得肥佬不能到达自己所在的地方。并且为了防止意外,他还想知道有多少个这样的节目。
简而言之,恶梦想知道对于任意一个节目p∈[1,N],有多少个节目t,使得删掉t之后,不存在一条从节目1出发到节目p的路径。注意,节目1和节目p也是可以被删的。
由于他非常的忙碌,所以他把这个任务交给了你。

对于15%的数据,N <= 100
对于30%的数据,N <= 800
对于50%的数据,N <= 5000
对于70%的数据,N <= 10000
对于100%的数据,N <= 50000

Solution


70p暴力连边跑拓扑排序+LCA求支配树,还要啥自行车(滑稽

由于每次连的都是一整段区间,我们考虑线段树优化建图。我们从大到小插入a建n棵线段树,每个节点向儿子分别连边,每个节目向可行节点连边,每个节点向历史版本连边,然后拓扑排序即可

需要注意的是,历史版本可能不存在,这个时候不能连( ╯□╰ )

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#define rep(i,st,ed) for (register int i=st;i<=ed;++i)
#define drp(i,st,ed) for (register int i=st;i>=ed;--i)

const int N=50005*20;
const int E=50005*100;

struct edge {int y,next;} e[E];
struct treeNode {int l,r;} t[N];

int fa[N][18],d[N],dep[N],s[N];
int a[N],L[N],R[N],rt[N];
int ls[N],edCnt,n,tot;

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

void add_edge(int x,int y) {
	e[++edCnt]=(edge) {y,ls[x]};
	ls[x]=edCnt; d[y]++;
}

int get_lca(int x,int y) {
	if (dep[x]<dep[y]) std:: swap(x,y);
	drp(i,16,0) if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];
	if (x==y) return x;
	drp(i,16,0) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}

void top_sort() {
	static int q[N];
	int h=1,t=0;
	rep(i,2,n+tot) if (!d[i]) {
		q[++t]=i;
	}
	for (;h<=t;) {
		int x=q[h++]; s[x]=-1;
		for (int i=ls[x];i;i=e[i].next) {
			int y=e[i].y;
			if (!(--d[y])) q[++t]=y;
		}
	}
	dep[1]=1; q[1]=1;
	for (h=1,t=1;h<=t;) {
		int x=q[h++];
		for (int i=ls[x];i;i=e[i].next) {
			int y=e[i].y;
			if (!fa[y][0]) fa[y][0]=x;
			else fa[y][0]=get_lca(fa[y][0],x);
			if (!(--d[y])) {
				q[++t]=y; s[y]+=s[fa[y][0]];
				dep[y]=dep[fa[y][0]]+1;
				rep(i,1,16) fa[y][i]=fa[fa[y][i-1]][i-1];
			}
		}
	}
}

void modify(int &now,int pre,int tl,int tr,int x,int v) {
	t[now=++tot]=t[pre];
	if (pre) add_edge(now+n,pre+n);
	if (tl==tr) {
		add_edge(now+n,v);
		return ;
	}
	int mid=(tl+tr)>>1;
	if (x<=mid) {
		modify(t[now].l,t[pre].l,tl,mid,x,v);
		add_edge(now+n,t[now].l+n);
	} else {
		modify(t[now].r,t[pre].r,mid+1,tr,x,v);
		add_edge(now+n,t[now].r+n);
	}
}

void query(int now,int tl,int tr,int l,int r,int x) {
	if (r<l||!now) return ;
	if (tl>=l&&tr<=r) {
		add_edge(x,now+n);
		return ;
	}
	int mid=(tl+tr)>>1;
	query(t[now].l,tl,mid,l,std:: min(r,mid),x);
	query(t[now].r,mid+1,tr,std:: max(mid+1,l),r,x);
}

int main(void) {
	n=read();
	rep(i,1,n) a[i]=read();
	rep(i,1,n) L[i]=read(),R[i]=read();
	drp(i,n,1) {
		query(rt[i+1],1,n,L[i],R[i],i);
		if (i!=1) modify(rt[i],rt[i+1],1,n,a[i],i);
	}
	rep(i,1,n) s[i]=1;
	top_sort();
	rep(i,1,n) printf("%d\n", s[i]);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值