Loading

[JSOI2019]精准预测

传送门

题目大意

现在有 \(n\) 个人和 \(m\) 条限制,对于每条限制,包含一个 \(c,t,x,y\)。总时间是 \(T\)

\(c=0\),则表示如果在 \(t\) 时刻第 \(x\) 个人死了,那么在 \(t+1\) 时刻第 \(y\) 个人也死了。
\(c=1\),则表示如果在 \(t\) 时刻第 \(x\) 个人活着,那么在 \(t\) 时刻第 \(y\) 个人就死了。

现在你需要对每个人求出在 \(T+1\) 可能与它同时活着的人的个数。如果这个人在 \(T+1\) 一定死了,那么输出 0

Solution

首先考虑一个暴力,这是一个非常显然的 2-SAT 问题。我们把每个人分成 \(T+1\) 个,对于二元组 \((x,t)\) 表示第 \(x\) 个人的 \(t\) 时刻。那么对于每个二元组,只有两种情况——活着或者死了,并且是相斥的两种状态。

考虑对一些限制连边。
我们用 \((x,t,0)\) 表示 \((x,t)\) 的死亡状态点,\((x,t,1)\) 表示 \((x,t)\) 的存活状态点。那么有以下一些连边。

  1. \((x,t,0)\to (x,t+1,0)\),表示如果 \(t\) 时刻 \(x\) 已经挂了,那么 \(t+1\) 一定挂了。
  2. \((x,t,1)\to (x,t-1,1)\),同理,表示 \(t\) 时刻 \(x\) 活着意味着 \(t-1\) 也一定活着。
  3. 对于题目中第一种限制,有 \((x,t,0)\to (y,t+1,0)\) 和逆否命题 \((y,t+1,1)\to (x,t,1)\)
  4. 对于题目中第二种限制,有 \((x,t,1)\to (y,t,0)\) 和逆否命题 \((y,t,1)\to (x,t,0)\)

首先明确不需要 Tarjan,因为容易看出已经是 DAG 了。

然后考虑题目要求什么。实际上是对每个点 \(x\),从 \((x,T+1,1)\) 能到达多少个 \((y,T+1,0)\),然后 \(n-cnt-1\) 就是这个点的答案。什么意思?就是假设 \(x\) 是活着的,那么能得到一些在 \(T+1\) 必然死掉的人,然后用总人数减去这个人数再刨去自己就是可能与 \(x\) 同时存活的人数了。


然后优化这个残忍的暴力,考虑上面的光是建图就是 \(O(nT)\) 了,非常不靠谱。那考虑把一些没有用的点杀了,首先一点就是没有在询问中出现的点,一定是没有用的。于是考虑对每个点 \(x\) 开一个 vector 存它出现过的时刻,然后建图的时候只存这几个点就好了。

这样点数是 \(O(2m+n)\) 的,其实仔细一想可以更优,询问中出现了 \(x\)\(y\),其实只要 \(x\)\(t\) 就行了,\(y\) 没有必要。因为如果 \(y\) 后续没有作为 \(x\) 出现在询问中的话,对最后结果没有影响,可以直接用大于它的最小出现的那个节点代替它。是一样的。(不过这没有关系,没看懂也不要紧)

这样点数就优化下来了,同时边数也下来了。


然后考虑空间的问题,发现如果对每个节点暴力跑肯定是会炸的,那么容易想到用 bitset 优化这个过程。可是如果对每个节点开一个大小为 \(n\) 的 bitset 我是想都不敢想。所以这里用到一个小 trick——平衡复杂度,既然两边一边是空间大,一边是时间大,那么何不平衡一波。我们考虑把 \(n\) 个人分批处理,每次处理 \(S\) 个人,这样空间复杂度就成了 \(O(\dfrac{nS}{8})\),卡卡可以过去,时间就是 \(O(\dfrac{n(n+m)}{S})\)\(S=10000\) 可以过去。

Code

#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb push_back
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
#define pll pair<ll,ll>
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
const int MAXN=1e6+10;
const int MAXM=300010;
vector<int> used[MAXN],e[MAXM];
map<int,int> mp[MAXN][2];
bitset<10010> dp[MAXM],tmp;
int tot=0,deg[MAXM];
void sprd(){
	queue<int> q;
	rep(i,1,tot) for(int s:e[i]) deg[s]++;
	rep(i,1,tot) if(!deg[i]) q.push(i);
	while(!q.empty()){
		int x=q.front();q.pop();
		for(int s:e[x]){
			dp[s]|=dp[x];
			if(--deg[s]==0) q.push(s);
		}
	}
}
struct Query{
	int c,t,x,y;
	void input(){
		cin>>c>>t>>x>>y;
		used[x].pb(t);
	}
}q[MAXN];
int ans[MAXN];
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int T,n,m;
	cin>>T>>n>>m;
	rep(i,1,m) q[i].input();
	rep(i,1,n) used[i].pb(T+1),sort(used[i].begin(),used[i].end()),
	used[i].erase(unique(used[i].begin(),used[i].end()),used[i].end());
	rep(i,1,n){
		for(int t:used[i]) mp[i][0][t]=++tot,mp[i][1][t]=++tot;
		rep(j,0,(int)used[i].size()-1){
			if(j) e[mp[i][1][used[i][j-1]]].pb(mp[i][1][used[i][j]]);
			if(j<(int)used[i].size()-1)
				e[mp[i][0][used[i][j+1]]].pb(mp[i][0][used[i][j]]);
		}
	}
	rep(i,1,m){
		if(q[i].c==0){
			int v=upper_bound(used[q[i].y].begin(),used[q[i].y].end(),q[i].t)-used[q[i].y].begin();
			int to=used[q[i].y][v];
			e[mp[q[i].y][0][to]].pb(mp[q[i].x][0][q[i].t]);
			e[mp[q[i].x][1][q[i].t]].pb(mp[q[i].y][1][to]);
//			cerr<<mp[q[i].y][0][to]<<' '<<mp[q[i].x][0][q[i].t]<<'\n';
//			cerr<<mp[q[i].x][1][q[i].t]<<' '<<mp[q[i].y][1][to]<<'\n';
		}else{
			int v=lower_bound(used[q[i].y].begin(),used[q[i].y].end(),q[i].t)-used[q[i].y].begin();
			int to=used[q[i].y][v];
			e[mp[q[i].y][0][to]].pb(mp[q[i].x][1][q[i].t]);
			e[mp[q[i].x][0][q[i].t]].pb(mp[q[i].y][1][to]);
//			cerr<<mp[q[i].y][0][to]<<' '<<mp[q[i].x][1][q[i].t]<<'\n';
//			cerr<<mp[q[i].x][0][q[i].t]<<' '<<mp[q[i].y][1][to]<<'\n';
		}
	}
	rep(i,1,n) ans[i]=n-1;
	for(int l=1,r;l<=n;){
		r=min(l+9999,n);
		rep(i,1,tot) dp[i].reset();
		rep(i,l,r) dp[mp[i][0][T+1]][i-l+1]=1;
		sprd();tmp.reset();
		rep(i,l,r) if(dp[mp[i][1][T+1]][i-l+1]) ans[i]=0,tmp[i-l+1]=1;
		rep(i,1,n) dp[mp[i][1][T+1]]|=tmp;
		rep(i,1,n) if(ans[i]) ans[i]-=dp[mp[i][1][T+1]].count();
		l=r+1;
	}
	rep(i,1,n) cout<<ans[i]<<' ';
	return 0;
}
posted @ 2022-03-04 20:03  ZCETHAN  阅读(30)  评论(0编辑  收藏  举报