首先,因为小于的关系是可传递的,所以对于一组限制((j_i,k_i)),我们考虑连一条有向边(j_i o 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;
}