【2022省选模拟】小 G 的 DAG——分块、Bitset

不提供链接

题目描述

在这里插入图片描述
在这里插入图片描述

题解

考虑对询问分块,在数组里存下最近的最多 B L = n BL=\sqrt{n} BL=n 次1操作,若数组满了就把操作拿出来一块处理。由于前面的操作不能作用于后面的操作,所以我们从后往前暴力地处理操作,规定一个点只能访问一次,总共是 O ( n ) O(n) O(n) 的。

对于操作2,显然只有某节点最后一次操作1之后的才有效。我们同样对询问分块,在每个块内的操作2,我们按值从小到大暴力处理,同样是 O ( n ) O(n) O(n) 的。

对于询问,我们可以先求出最后一次覆盖的操作1的时间,然后询问后面的操作2,用整块直接访问、边角暴力判断即可。

剩下的问题是,我们需要每次 O ( 1 ) O(1) O(1) 地判断某个点能否到达询问点,这很容易想到用 B i t s e t Bitset Bitset。但是直接的 B i t s e t Bitset Bitset 时间空间都是 O ( n 2 w ) O(\frac{n^2}{w}) O(wn2) 的,根本过不了。我们先考虑怎么降下空间:再对询问分个块,每次对块长这么多个询问点一起做一次拓扑+ B i t s e t Bitset Bitset,那么 B i t s e t Bitset Bitset 的大小只用开到块长。

此时的总时间是 O ( n ∗ 询 问 个 数 w + q n ) O(\frac{n*询问个数}{w}+q\sqrt{n}) O(wn+qn )
还是一副不可过的样子,但是由于最大数据要保证操作数都十分充足,导致询问数很少(好像只有大约 n 10 \frac{n}{10} 10n)的样子,所以卡卡就过了。

代码

#include<bits/stdc++.h>//JZM yyds!!
#define ll long long
#define uns unsigned
#define IF (it->first)
#define IS (it->second)
#define END putchar('\n')
using namespace std;
const int MAXN=100005;
const ll INF=1e17;
inline ll read(){
	ll x=0;bool f=1;char s=getchar();
	while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
	while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
	return f?x:-x;
}
int ptf[50],lpt;
inline void print(ll x,char c='\n'){
	if(x<0)putchar('-'),x=-x;
	ptf[lpt=1]=x%10;
	while(x>9)x/=10,ptf[++lpt]=x%10;
	while(lpt)putchar(ptf[lpt--]^48);
	if(c>0)putchar(c);
}
inline ll lowbit(ll x){return x&-x;}

const int M=64,B=4096;
struct BS{
	uns ll a[M];BS(){}
	BS(int x){memset(a,0,sizeof(a)),a[x/M]|=(1ull<<x%M);}
	inline bool AT(int x){return (a[x/M]>>x%M)&1;}
	inline BS operator|(const BS&b)const{
		BS res;
		for(int i=0;i<M;i++)res.a[i]=a[i]|b.a[i];
		return res;
	}
}EM,b[MAXN];
struct itn{
	int op,x;ll d;itn(){}
	itn(int O,int X,ll D){op=O,x=X,d=D;}
}st[MAXN],ts[MAXN],q2[MAXN];
int n,m,Q,le,el;
struct edge{
	int v,to;edge(){}
	edge(int V,int T){v=V,to=T;}
}e[MAXN<<1];
int EN,G[MAXN],D[MAXN],du[MAXN];
inline void addedge(int u,int v){
	e[++EN]=edge(v,G[u]),G[u]=EN;
	e[++EN]=edge(u,D[v]),D[v]=EN;
}
queue<int>q;
int dfn[MAXN],la[MAXN],id[MAXN],IN;
ll val[MAXN],bv[320][MAXN];
vector<itn>q1[MAXN];
inline void solve(int l){
	IN=0;
	for(int i=1;i<=n;i++)id[i]=0;
	for(int i=l;i<=Q&&IN<B;i++)
		if(q2[i].op==3&&!id[q2[i].x])id[q2[i].x]=++IN;
	for(int i=1;i<=n;i++){
		du[i]=0;
		if(id[i]>0)b[i]=BS(id[i]-1);
		else b[i]=EM;
	}
	if(!IN)return;
	for(int i=2;i<=EN;i+=2)du[e[i].v]++;
	for(int i=1;i<=n;i++)if(!du[i])q.push(i);
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=D[u];i;i=e[i].to){
			int v=e[i].v;
			b[v]=b[v]|b[u],du[v]--;
			if(!du[v])q.push(v);
		}
	}
}
inline bool rec(int x,int y){
	if(!id[y])return 0;
	return b[x].AT(id[y]-1);
}
signed main()
{
	freopen("dag.in","r",stdin);
	freopen("dag.out","w",stdout);
	n=read(),m=read(),Q=read();
	for(int i=1,u,v;i<=m;i++)u=read(),v=read(),addedge(u,v);
	int BL=316;
	for(int i=1;i<=Q;i++){
		q2[i].op=read(),q2[i].x=read();
		if(q2[i].op<3)q2[i].d=read();
	}
	solve(1);
	for(int fn=1;fn<=Q;fn++){
		int op=q2[fn].op,x=q2[fn].x;q2[fn].x=0;
		if(op==1)st[++le]=itn(fn,x,q2[fn].d);
		else if(op==2)ts[++el]=itn(fn,x,q2[fn].d),q2[fn]=ts[el];
		else{
			ll ans=val[x];
			for(int i=1;i<=le;i++)if(rec(st[i].x,x))
				ans=st[i].d,la[x]=st[i].op;
			for(int i=la[x];i<=fn;){
				if(i%BL==0&&i-BL>=la[x])ans=min(ans,bv[(i-1)/BL][x]+INF);
				else if(q2[i].x&&rec(q2[i].x,x))ans=min(ans,q2[i].d);
				if(i%BL==0&&i+BL<=fn)i+=BL;
				else i++;
			}
			print(ans);
		}
		if(fn<Q&&q2[fn+1].op==3&&!id[q2[fn+1].x])solve(fn+1);
		if(fn%BL==0){
			int x=(fn-1)/BL;
			sort(ts+1,ts+1+el,[](itn a,itn b){return a.d<b.d;});
			for(int i=1;i<=el;i++){
				while(!q.empty())q.pop();
				if(bv[x][ts[i].x]+INF>ts[i].d)
					bv[x][ts[i].x]=ts[i].d-INF,q.push(ts[i].x);
				while(!q.empty()){
					int u=q.front();q.pop();
					for(int j=G[u];j;j=e[j].to){
						int v=e[j].v;
						if(bv[x][v]+INF<=ts[i].d)continue;
						bv[x][v]=ts[i].d-INF,q.push(v);
					}
				}
			}el=0;
		}
		if(le<=BL)continue;
		for(int i=1;i<=n;i++)dfn[i]=0;
		for(int i=le;i>0;i--){
			while(!q.empty())q.pop();
			if(dfn[st[i].x]<i)dfn[st[i].x]=i,q.push(st[i].x);
			while(!q.empty()){
				int u=q.front();q.pop();
				val[u]=st[i].d,la[u]=st[i].op;
				for(int j=G[u];j;j=e[j].to){
					int v=e[j].v;
					if(dfn[v]>=i)continue;
					dfn[v]=i,q.push(v);
				}
			}
		}le=0;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值