Luogu P5332 [JSOI2019]精准预测
Link
对于火星人\(u\),在时间点\(t\),我们将其拆成两个点\((u,t,0),(u,t,1)\)表示它的存活状态。
考虑用\(\text{2-SAT}\)来表示限制。
那么此时我们有\((i,t,0)\rightarrow(i,t+1,0),(i,t+1,1)\rightarrow(i,t,1)\)两条边。
对于限制\((0,t,u,v)\),我们连\((u,t,0)\rightarrow(v,t+1,0),(v,t+1,1)\rightarrow(u,t,1)\)。
对于限制\((1,t,u,c)\),我们连\((u,t,1)\rightarrow(v,t,0),(v,t,1)\rightarrow(u,t,0)\)。
那么我们要求的就是在选择\((u,T+1,0)\)时有多少个\((v,T+1,1)\)必定被选择,即有多少个\((v,T+1,1)\)在\((u,T+1,0)\)的后继中。
注意到对于每个火星人而言,我们可以只保留它的在时刻\(T+1\)的点和作为\(u\)限制时的点,那么点数就是\(2(n+m)\)了。
求答案用\(\text{bitset}\)优化\(\text{DAG}\)上传递闭包即可,时空复杂度均为\(O(\frac{n^2}{\omega})\)(认为\(n,m\)同阶)。
空间开不下可以分多次跑。
#include<map>
#include<queue>
#include<bitset>
#include<cctype>
#include<cstdio>
#include<vector>
#include<algorithm>
const int N=300007;
int read(){int x=0,c=getchar();while(isspace(c))c=getchar();while(isdigit(c))(x*=10)+=c&15,c=getchar();return x;}
int pos[N],deg[N],ans[N];struct node{int c,t,x,y;}a[N];
std::queue<int>q;std::vector<int>e[N];std::map<int,int>mp[N];std::bitset<10001>s[N],ban;
void add(int c,int u,int v){e[u+c].push_back(v),e[v+1].push_back(u+!c);}
int main()
{
int t=read(),n=read(),m=read(),cnt=0;
for(int i=1;i<=m;++i) a[i]={read(),read(),read(),read()},mp[a[i].x][a[i].t]=cnt,cnt+=2;
for(int i=1;i<=n;++i) mp[i][t+1]=cnt,cnt+=2;
for(int i=1;i<=m;++i) add(a[i].c,mp[a[i].x][a[i].t],(a[i].c? mp[a[i].y].lower_bound(a[i].t):mp[a[i].y].upper_bound(a[i].t))->second);
for(int i=1;i<=n;pos[i]=prev(mp[i].end())->second,++i) for(auto l=mp[i].begin(),r=next(l);r!=mp[i].end();l=r,r=next(r)) add(0,l->second,r->second);
for(int l=1,r;l<=n;l+=10000)
{
r=std::min(l+9999,n),ban.reset();
for(int u=0;u<cnt;++u)
{
s[u].reset();
for(int v:e[u]) ++deg[v];
}
for(int i=l;i<=r;++i) s[pos[i]+1][i-l]=1;
for(int u=0;u<cnt;++u) if(!deg[u]) q.push(u);
for(int u;!q.empty();)
{
u=q.front(),q.pop();
for(int v:e[u]) if(s[v]|=s[u],!--deg[v]) q.push(v);
}
ban.reset();
for(int i=l;i<=r;++i) if(s[pos[i]][i-l]) ban[i-l]=1,ans[i]=-1e9;
for(int i=1;i<=n;++i) ans[i]+=r-l+1-(s[pos[i]]|ban).count();
}
for(int i=1;i<=n;++i) printf("%d ",std::max(ans[i]-1,0));
}