【JSOI2019】精准预测(2-SAT)(拓扑排序)(bitset卡常)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/zxyoi_dreamer/article/details/102748707

传送门


省选考常数优化,卡空间卡复杂度?惊了

题解:

读完题知道很显然的2-SAT模型。

每个人按照时间建立若干个点,表示他在这个时刻是否存活,然后按照题目给的关系连有向边(注意不要忘记连反向边)。存活时间向前连边,表示在这之前它都是活的,死亡时间向后连边,表示从此刻开始都是死亡状态。

很容易发现由于我们都是规定在某个条件下某个人必死,所以图是一个DAG。

要最大化一个人存活时候总存活人数,那么就是要求他存活的时候非死不可的人数,当然要是他本身就无法存活要另说。

换句话说就是要求后继中存在某个集合的点的数量。

很显然只能上bitset。

容易注意到空间开不下,很多题解是多跑几次,但是直接动态分配内存就行了,容易证明在内存峰值也最多只有 O ( n ) O(n) 个bitset。

然后有一些常数优化的细节,首先存活情况之间的限制连边可以不连,然后每个点只在它作为限制的时候建点,可以把点数优化到 2 n + 2 m 2n+2m ,边数(算上所有反向边)不会超过 4 m 4m 只会更少。

实现上也没什么细节,随便写写就行了


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{
	inline char gc(){
		static cs int Rlen=1<<22|7;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	template<typename T>
	inline T get(){
		char c;T num;
		while(!isdigit(c=gc()));num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int gi(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;
using pii=std::pair<int,int>;
#define fi first
#define se second

cs int N=5e4+7,M=1e5+7,S=2*N+2*M;
typedef std::bitset<N> B;
B fail,*p[N],*s[S];
int T,n,m,ct=0;
struct node{int op,t,u,v;}qry[M];
std::vector<pii> a[N];
int d[S],liv[S],die[S];
std::vector<int> G[S];
inline void adde(int u,int v){
	G[u].push_back(v);++d[v];
	G[v^1].push_back(u^1);++d[u^1];
}
int q[S],qn;

signed main(){
#ifdef zxyoi
	freopen("predict.in","r",stdin);
#endif
	T=gi(),n=gi(),m=gi();
	for(int re i=1;i<=n;++i)a[i].push_back(pii(T+1,0));
	for(int re i=1;i<=m;++i){
		int op=gi(),t=gi(),u=gi(),v=gi();
		a[u].push_back(pii(t,0));
		qry[i]=(node){op,t,u,v};
	}
	for(int re i=1;i<=n;++i){
		std::sort(a[i].begin(),a[i].end());
		a[i].erase(std::unique(a[i].begin(),a[i].end()),a[i].end());
		for(auto &t:a[i])t.se=ct,ct+=2;
		for(int re j=0;j+1<a[i].size();++j)
		adde(a[i][j].se^1,a[i][j+1].se^1);
		liv[a[i].back().se]=i;
		die[a[i].back().se^1]=i;
	}
	for(int re i=1;i<=m;++i){
		int op=qry[i].op,t=qry[i].t,u=qry[i].u,v=qry[i].v;
		auto p=std::lower_bound(a[u].begin(),a[u].end(),pii(t,-1));
		auto q=std::lower_bound(a[v].begin(),a[v].end(),pii(t+(op==0),-1));
		adde(p->se^(op==0),q->se^1);
	}
	for(int re i=0;i<ct;++i)if(!d[i])q[++qn]=i;
	for(int re i=1;i<=qn;++i){
		int u=q[i];if(s[u]==nullptr)s[u]=new B;
		bool ok=true;
		if(liv[u])s[u]->set(liv[u]);
		if(die[u]){
			if(!s[u]->test(die[u]))
				p[die[u]]=s[u],ok=false;
			else fail.set(die[u]); 
		}
		for(int re v:G[u]){
			if(!--d[v])q[++qn]=v;
			if(s[v])*s[v]|=*s[u];
			else if(ok)s[v]=s[u],ok=false;
			else s[v]=new B,*s[v]=*s[u];
		}
		if(ok)delete s[u];s[u]=nullptr;
	}
	for(int re u=1;u<=n;++u){
		if(fail[u])cout<<"0 ";
		else {
			*p[u]|=fail;
			cout<<n-1-p[u]->count()<<" ";
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/102748707