题解 CF1344C Quantifier Question

题目链接

首先,因为小于的关系是可传递的,所以对于一组限制\((j_i,k_i)\),我们考虑连一条有向边\(j_i\to k_i\)。这样能建出一个图。显然。如果图上有环,肯定无解,答案是\(-1\)。而只要图上没有环,那么一定有解,因为至少全填\(\exist\)的时候是成立的。所以以下只考虑一个DAG的情况。

接下来,要让\(\forall\)尽可能多。这就需要你细细地品这个关系了。

你会发现,每个\(Q_i\),不是平等的。例如,如果是\(x_1<x_2,x_3<x_2\),答案就是\(Q_1=\forall,Q_2=\exist,Q_3=\exist\)。但如果是\(x_1<x_3,x_2<x_3\),答案就可以变成\(Q_1=\forall,Q_2=\forall,Q_3=\exist\)。(备注:这个例子其实就是样例3)。你可以看出,在两个一模一样的结构里,仅仅是编号不同(\(2\)\(3\)交换了),就可以改变\(\forall\)的数量,所以从前往后每个编号是有区别的、不平等的。

进而发现,对于\(i<j\),只要\(i\)能到达\(j\)\(j\)能到达\(i\),那么\(Q_j\)一定等于\(\exist\),不论\(Q_i\)等于什么。那既然不论我填\(\forall\)还是\(\exist\),影响是一样的,我当然尽可能多填\(\forall\)。也就是用这样一种贪心的思想:能填\(\forall\)就绝不填\(\exist\)

于是问题转化为,按编号从小到大,依次标记每个节点。对当前节点\(i\),如果所有【\(i\)能到达的】和【能到达\(i\)的】节点都未被标记,则\(Q_i=\forall\),否则\(Q_i=\exist\)

当然,你会发现,对所有【\(i\)能到达的】和【能到达\(i\)的】节点进行查询,非常困难。转化一下,变成修改操作,也同样困难!如果你这么想这个问题,那你就是用了“数据结构”的思维,总想在过程进行中,大力维护一些什么。但其实,还有另一种思维,也就是“DP”的思维。不在过程中维护,而是预处理出来:用递推的方式预处理出来,不断从已知的,推出未知的。

具体来说,可以现在DAG上正着跑一遍拓扑排序,就能递推出【能到达每个点的】最小编号。再在反图上跑一遍拓扑排序,就能递推出【每个点能到达的】最小编号。

时间复杂度\(O(n+m)\)

参考代码:

//problem:
#include <bits/stdc++.h>
using namespace std;

#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;

template<typename T>inline void ckmax(T& x,T y){x=(y>x?y:x);}
template<typename T>inline void ckmin(T& x,T y){x=(y<x?y:x);}

const int MAXN=2e5;
int n,m,ind[MAXN+5],ind_rev[MAXN+5];
int dp[MAXN+5],dp_rev[MAXN+5];
vector<int>G[MAXN+5],RG[MAXN+5];

char ans[MAXN+5];
int maxnum;
int main() {
	cin>>n>>m;
	for(int i=1;i<=m;++i){
		int u,v; cin>>u>>v;
		G[u].pb(v); RG[v].pb(u);
		ind[v]++; ind_rev[u]++;
	}
	queue<int>q;
	for(int i=1;i<=n;++i){
		dp[i]=i;
		if(!ind[i]){
			q.push(i);
		}
	}
	while(!q.empty()){
		int u=q.front(); q.pop();
		for(int i=0;i<SZ(G[u]);++i){
			int v=G[u][i];
			ind[v]--;
			ckmin(dp[v],dp[u]);
			if(!ind[v]){
				q.push(v);
			}
		}
	}
	for(int i=1;i<=n;++i)
		if(ind[i]){
			cout<<"-1"<<endl;
			return 0;
		}
	for(int i=1;i<=n;++i){
		dp_rev[i]=i;
		if(!ind_rev[i]){
			q.push(i);
		}
	}
	while(!q.empty()){
		int u=q.front(); q.pop();
		for(int i=0;i<SZ(RG[u]);++i){
			int v=RG[u][i];
			ind_rev[v]--;
			ckmin(dp_rev[v],dp_rev[u]);
			if(!ind_rev[v]){
				q.push(v);
			}
		}
	}
	for(int i=1;i<=n;++i){
		if(dp[i]<i || dp_rev[i]<i){
			ans[i]='E';
		}
		else{
			ans[i]='A';
			maxnum++;
		}
	}
	cout<<maxnum<<endl;
	for(int i=1;i<=n;++i)
		cout<<ans[i];
	cout<<endl;
	return 0;
}
posted @ 2020-07-27 22:19  duyiblue  阅读(260)  评论(2编辑  收藏  举报