zoukankan      html  css  js  c++  java
  • 题解-Quantifier Question

    Quantifier Question

    有长度为 (n) 的序列 (x{n}),有 (m) 个条件 ((j_i,k_i))。有 (n) 个待定的条件符 (Q_iin{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) 的数量及整个条件符串。

    数据范围:(2le nle 2cdot 10^5)(1le mle 2cdot 10^5)(1le j_i,k_ile n,j_i ot=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 o k_i)。然后拓扑排序。如果拓扑排序序列长度 (<n),说明有环,无解,输出 (-1)。否则存下这个拓扑序列。

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

    然后用类似 ( exttt{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
    ",sm);
    	for(int i=1;i<=n;i++) putchar("EA"[ans[i]]);puts("");
    	return 0;
    }
    

    祝大家学习愉快!

  • 相关阅读:
    MFC列表控件更改一行的字体颜色
    MFC之sqlite
    MFC笔记10
    MFC---关于string.h相关函数
    MFC笔记8
    MFC笔记7
    MFC笔记6
    MFC笔记5
    MFC笔记4
    MFC笔记3
  • 原文地址:https://www.cnblogs.com/Wendigo/p/12845268.html
Copyright © 2011-2022 走看看