Luogu

LOJ

分析

每个人在每个时刻只有 $2$ 种状况,考虑 2-SAT 。

设 $(x,t,0/1)$ 表示 $x$ 在 $t$ 时刻活着/死了。那么直接连边就好了。

另外还有一个隐藏条件:如果 $x$ 在 $t$ 时刻活着,那么在 $t-1$ 时刻也活着;如果 $x$ 在 $t$ 时刻死了,那么 $x$ 在 $t+1$ 时刻也死了。这个同样要连边。

问题变为在建好的图上,从 $(i,T+1,0)$ 出发,可以到达多少个 $(j,T+1,1)$ 。假设有 $ans$ 个,那么答案为 $n-ans-1$ 。

这个东西怎么求呢?可以用 $\mathrm{bitset}$ 维护一下,然后拓扑排序转移。时间复杂度为 $O(\frac{Tnm}{\omega})$ 。

这样子显然过不去,原因是点数太多了。考虑怎么优化点数。

显然只有至多 $4m$ 个点是有用的(每个预言只和 $4$ 个点有关)。那么把这 $4m$ 个点拿出来建图就行了。

注意上面的隐藏条件也要建:对于 $x$ 相同的点,按 $t$ 排序,然后依次连边。

这样子 $\mathrm{bitset}$ 还是存不下,可以分段跑。

另外还有一些要注意的地方:

  • 如果从 $(i,T+1,0)$ 可以到达 $(i,T+1,1)$ ,那么说明 $i$ 必死。显然此时 $i$ 的答案为 $0$ 。另外这样的 $i$ 也不会对别的点产生贡献,注意判掉。
  • 对于某些人,可能没有与他有关的预言,那么他最后一定可以活着。注意处理这一部分。

具体实现和细节见代码。

代码

// ===================================
//   author: M_sea
//   website: http://m-sea-blog.com/
// ===================================
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <bitset>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <map>
#define re register
using namespace std;

inline int read() {
    int X=0,w=1; char c=getchar();
    while (c<'0'||c>'9') c=getchar();
    while (c>='0'&&c<='9') X=X*10+c-'0',c=getchar();
    return X*w;
}

const int N=400000+10;
const int B=10000;

struct Edge { int v,nxt; } e[N<<1];
int head[N];

inline void addEdge(int u,int v) {
    static int cnt=0;
    e[++cnt]=(Edge){v,head[u]},head[u]=cnt;
}

int n,m,T,tot;
bitset<B> bit[N],tmp;
map<pair<int,int>,int> mp;
vector<pair<int,int>> same[N];
queue<int> Q;
int lst[N],now[N],deg[N],tag[N],ans[N];

inline int id(int x,int y) { return (x<<1)+y-1; }
inline int calcid(int x,int y) {
    map<pair<int,int>,int>::iterator it=mp.find(make_pair(x,y));
    if (it==mp.end()) {
        int res=++tot;
        mp[make_pair(x,y)]=res;
        same[x].push_back(make_pair(y,res));
        return res;
    }
    else return it->second;
}

int main() {
    T=read(),n=read(),m=read();
    for (re int i=1;i<=m;++i) {
        int op=read(),t=read(),x=read(),y=read();
        if (!op) {
            x=calcid(x,t),y=calcid(y,t+1);
            addEdge(id(x,0),id(y,0)),++deg[id(y,0)];
            addEdge(id(y,1),id(x,1)),++deg[id(x,1)];
        } else {
            x=calcid(x,t),y=calcid(y,t);
            addEdge(id(x,1),id(y,0)),++deg[id(y,0)];
            addEdge(id(y,1),id(x,0)),++deg[id(x,0)];
        }
    }
    for (re int i=1;i<=n;++i) {
        sort(same[i].begin(),same[i].end());
        for (re int j=1;j<same[i].size();++j) {
            int u=same[i][j-1].second,v=same[i][j].second;
            addEdge(id(u,0),id(v,0)),++deg[id(v,0)];
            addEdge(id(v,1),id(u,1)),++deg[id(u,1)];
        }
        if (same[i].size()) lst[i]=(--same[i].end())->second;
        else lst[i]=-1;
    }
    for (re int l=1,r=min(B,n);l<=n;l=r+1,r=min(l+B-1,n)) {
        for (re int i=1;i<=(tot<<1);++i) bit[i].reset();
        for (re int i=l;i<=r;++i)
            if (~lst[i]) bit[id(lst[i],1)].set(i-l);
        for (re int i=1;i<=(tot<<1);++i) {
            now[i]=deg[i];
            if (!now[i]) Q.push(i);
        }
        while (!Q.empty()) {
            int u=Q.front(); Q.pop();
            for (re int i=head[u];i;i=e[i].nxt) {
                int v=e[i].v; bit[v]|=bit[u],--now[v];
                if (!now[v]) Q.push(v);
            }
        }
        tmp.reset();
        for (re int i=l;i<=r;++i)
            if (~lst[i]&&bit[id(lst[i],0)][i-l])
                tag[i]=1,tmp.set(i-l);
        for (re int i=1;i<=n;++i) {
            if (~lst[i]) ans[i]+=(bit[id(lst[i],0)]|tmp).count();
            else ans[i]+=tmp.count();
        }
    }
    for (re int i=1;i<=n;++i) printf("%d ",tag[i]?0:n-ans[i]-1);
    puts("");
    return 0;
}
最后修改:2021 年 03 月 23 日 06 : 25 PM