Loading

题解-Quantifier Question

Quantifier Question

有长度为 \(n\) 的序列 \(x\{n\}\),有 \(m\) 个条件 \((j_i,k_i)\)。有 \(n\) 个待定的条件符 \(Q_i\in\{\forall,\exists\}\),使

\[Q_1x_1,Q_2x_2,...,Q_nx_n,(x_{j_1}<x_{k_1})∧(x_{j_2}<x_{k_2})∧\cdots∧(x_{j_m}<x_{k_m}) \]

\(\forall\) 最多的方案。如果没有满足方案,输出 \(-1\)。否则输出 \(\forall\) 的数量及整个条件符串。

数据范围:\(2\le n\le 2\cdot 10^5\)\(1\le m\le 2\cdot 10^5\)\(1\le j_i,k_i\le n,j_i\not=k_i\)


一句话题解:拓扑排序,有环输出 \(-1\),否则将每个不被前后节点影响的节点置为 \(\forall\)


首先理解一下,不同序号的 \(Q_i\)有顺序的,不是平等的。如 \(x_1<x_2\)\(x_1<x_3\),令 \(Q_1=\exists,Q_2=\forall,Q_3=\forall\) 是不可以的(如选定 \(x_1\) 后取 \(x_2=x_1-1\) 就爆了);但是如果 \(x_3<x_1,x_3<x_2\),令 \(Q_1=\exists,Q_2=\forall,Q_3=\forall\) 是可以的

先用差分约束的思想,把条件转化为边,即连 \(j_i\to k_i\)。然后拓扑排序。如果拓扑排序序列长度 \(<n\),说明有环,无解,输出 \(-1\)。否则存下这个拓扑序列。

\(u<v\),因为先发挥 \(u\) 的条件符再发挥 \(v\) 的条件符。所以如果 \(u\) 可以走到 \(v\) 或者 \(v\) 可以走到 \(u\),那么 \(v\) 点就不能选 \(\forall\) 了;反之,如果对于 \(v\),不存在 \(u<v\) 可以走到它或被它走到,这样的节点条件符选 \(\forall\) 是最优且没有后效性的。

然后用类似 \(\texttt{dp}\) 的方法,求出每个节点的最小前驱(可以走到它)和最小后继(它可以走到),然后如果最小前驱和最小后继都不小于该节点编号,那么该节点的条件符选 \(\forall\)


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


代码:

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

//Start
typedef long long ll;
typedef double db;
#define mp(a,b) make_pair(a,b)
#define x(a) a.first
#define y(a) a.second
#define b(a) a.begin()
#define e(a) a.end()
#define sz(a) int((a).size())
#define pb(a) push_back(a)
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;

//Data
const int N=2e5;
int n,m,d[N+7],f[N+7],b[N+7],sm,ans[N+7];
vector<int> e[N+7],g[N+7];

//Bfs
int Bfs(vector<int>&q){ //就是拓扑排序
	//将图的拓扑序存入q,如果形成了环就返回0。
	q.clear();
	for(int i=1;i<=n;i++)if(!d[i]) q.pb(i);
	for(int i=0;i<sz(q);i++)
		for(int to:e[q[i]])if(!--d[to]) q.pb(to);
	return sz(q)==n;
}

//Main
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1,u,v;i<=m;i++) scanf("%d%d",&u,&v),e[u].pb(v),g[v].pb(u);
	for(int i=1;i<=n;i++) d[i]=sz(g[i]); //d为入度
	vector<int> tp;
	if(!Bfs(tp)) return puts("-1"),0;
	iota(f+1,f+n+1,1),iota(b+1,b+n+1,1); //将fi:=i,bi:=i。
	for(int i=0;i<=sz(tp)-1;i++)
		for(int to:g[tp[i]]) f[tp[i]]=min(f[tp[i]],f[to]); //最小前驱
	for(int i=sz(tp)-1;i>=0;i--)
		for(int to:e[tp[i]]) b[tp[i]]=min(b[tp[i]],b[to]); //最小后继
	for(int i=1;i<=n;i++)if(min(f[i],b[i])==i) sm++,ans[i]=1; //讲解中说了
	printf("%d\n",sm);
	for(int i=1;i<=n;i++) putchar("EA"[ans[i]]);puts("");
	return 0;
}

祝大家学习愉快!

posted @ 2020-05-07 19:59  George1123  阅读(253)  评论(0编辑  收藏  举报