zoukankan      html  css  js  c++  java
  • 【LOJ#6036】编码

    题面

    https://loj.ac/problem/6036

    题解

    很抱歉的告诉大家,这道题我思考了很长时间,还是不会。

    $yyb$代码的细节我也没有弄懂。我知道以后的学习中我会遇到很多这样的题,可能学习方式要进行转变了。

    我只能把我看明白的部分讲给大家听。

    首先是暴力,我们只要把$trie$树上具有“祖先-后代”关系的点对找出来,然后在他们之间连边,表示它们不能共存。

    其次是优化,我们利用$trie$树的形态优化建边。

    我们考虑对于每一个点,找到它祖先中所有的点连边(和“向子树内点连边等价”)。

    因为可能$trie$树上的一个位置可能躺着很多点,并且处于复杂度考虑,我们把“真正代表它自己的点”挂在$trie$树中它的位置之下。这样我们连边的时候只需要在$trie$树上找到所有它的祖先的位置,暴力连上去就好了(因为$sum_{i=1}^{n}{s_i} le 500000$),所以复杂度是对的。

    这样做还有一个问题,我们连边不能连到自己的反(这个条件是不确切的,确切的向下看),这样的话我们先建一个假点,如果发现要在它上面连边的时候直接建个新点上去,替换它的位置,再让新点向另一棵树中同一个位置的反,表示自己不能到这个点上,在把另一颗树的相同位置连向旧点,表示旧点的值和新点保持一致。这也是要预先排序的原因。

    upd2019.8.19:明白了,经$zsy$指导,我又找到了另一道题【UOJ#210寻找罪犯】,这两题有一个相似的条件,对于$trie$树上的同一个节点下只有一条命题成立,我们只需要建立一个新点,表示如果任意一个成立了,其他的都是假命题,也就是通过加点表示限制。

    #include<stack>
    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ri register int
    #define N 3000300
    using namespace std;
    inline int read() {
      int ret=0,f=0; char ch=getchar();
      while (ch<'0' || ch>'9') f|=(ch=='-'),ch=getchar();
      while (ch>='0' && ch<='9') ret*=10,ret+=(ch-'0'),ch=getchar();
      return f?-ret:ret;
    }
    vector<int> to[N];
    
    inline void add_edge(int u,int v) {
      to[u].push_back(v);
      u^=1; v^=1;
      to[v].push_back(u);
    }
    int dfn[N],low[N],clo,bel[N],cnt;
    stack<int> sta; bool ins[N];
    string s[N];
    
    void tarjan(int x) {
      dfn[x]=low[x]=++clo;
      sta.push(x); ins[x]=1;
      for (ri i=0;i<to[x].size();++i) {
        int y=to[x][i];
        if (dfn[y]) {
          if (ins[y]) low[x]=min(low[x],dfn[y]);
        }
        else {
          tarjan(y);
          low[x]=min(low[x],low[y]);
        }
      }
      if (low[x]==dfn[x]) {
        ++cnt;
        while (1) {
          int t=sta.top(); sta.pop();
          bel[t]=cnt; ins[t]=0;
          if (t==x) break;
        }
      }
    }
    
    int ch[N][2],tot;
    int n,len[N],id[N];
    char ss[N];
    
    void insert(int u,int p) {
      int x=n+1,lst=0;
      for (ri i=0;i<len[u];++i) {
        int c=s[u][i]-'0';
        if (!ch[x][c]) ch[x][c]=++tot;
        lst=x; x=ch[x][c]; add_edge(p,x<<1);
      }
      int y=++tot;
      add_edge(p,y<<1|1); add_edge(y<<1,x<<1);
      ch[lst][s[u][len[u]-1]-'0']=y;
    }
    bool cmp(int a,int b) {return len[a]<len[b];}
    
    int main() {
      n=read();
      for (ri i=1;i<=n;++i) {
        scanf("%s",ss);
        len[i]=strlen(ss);
        id[i]=i;
        for (ri j=0;j<len[i];++j) s[i]+=ss[j];
      }
      sort(id+1,id+n+1,cmp);
      tot=n+1;
      for (ri i=1;i<=n;++i) {
        int u=id[i];
        for (ri j=0;j<len[u];++j) 
        if (s[u][j]=='?') {
          s[u][j]='0'; insert(u,u<<1);
          s[u][j]='1'; insert(u,u<<1|1);
          break;
        }
        else if (j==len[u]-1) {
          insert(u,u<<1);
          add_edge(u<<1|1,u<<1);
        }
      }
      for (ri i=1;i<=(tot<<1|1);++i) 
      if (!dfn[i]) tarjan(i);
      for (ri i=1;i<=tot;++i)
      if (bel[i<<1]==bel[i<<1|1]) {
        puts("NO");
        return 0;
      }
      puts("YES");
      return 0;
    }
  • 相关阅读:
    TFS 2015(Visual Studio Team Foundation Server)的下载和安装
    又回来了
    java.net.SocketException:Too many open files 解决方案
    Java 正则判断一个字符串中是否包含中文
    zabbix 启动报错 cannot set resource limit: [13] Permission denied
    MySQL 查询某个数据库中所有包含数据记录的表名
    MySQL 判断数据库和数据表是否存在
    Java 实现 bash命令
    Java 实现 ssh命令 登录主机执行shell命令
    Java 实现 telnet命令 验证主机端口的连通性
  • 原文地址:https://www.cnblogs.com/shxnb666/p/11373870.html
Copyright © 2011-2022 走看看