传送门
省选考常数优化,卡空间卡复杂度?惊了
题解:
读完题知道很显然的2-SAT模型。
每个人按照时间建立若干个点,表示他在这个时刻是否存活,然后按照题目给的关系连有向边(注意不要忘记连反向边)。存活时间向前连边,表示在这之前它都是活的,死亡时间向后连边,表示从此刻开始都是死亡状态。
很容易发现由于我们都是规定在某个条件下某个人必死,所以图是一个DAG。
要最大化一个人存活时候总存活人数,那么就是要求他存活的时候非死不可的人数,当然要是他本身就无法存活要另说。
换句话说就是要求后继中存在某个集合的点的数量。
很显然只能上bitset。
容易注意到空间开不下,很多题解是多跑几次,但是直接动态分配内存就行了,容易证明在内存峰值也最多只有 O ( n ) O(n) O(n)个bitset。
然后有一些常数优化的细节,首先存活情况之间的限制连边可以不连,然后每个点只在它作为限制的时候建点,可以把点数优化到 2 n + 2 m 2n+2m 2n+2m,边数(算上所有反向边)不会超过 4 m 4m 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;
}