• 【LOJ#6036】[雅礼集训2017Day4]编码


    传送门

    题意简述

    判定 n 个含 ? 字符的二进制串是否存在一种把 0/1 填入 ? 中的方案使得任意两个串不具有前缀关系。
    (一个串最多一个 ?)

    Sol

    二进制串 ,并且一个串最多一个 '?'
    很容易想到用 2-sat 和 trie 树。
    那么问题变为插入这些二进制串,在 ‘?’ 处选择向哪边插入 ,使得任意一个结束节点的祖先节点中不存在一个结束节点。
    然后我们考虑构建 2-sat 模型,首先每一个串分配一个变量表示 '?' 选择了什么(没有?就随便强制选则某一个就行了)
    当然之后我们一种思路是直接用这些变量来判定并解决问题 ,不过复杂度显然是 (O(n^2)) 的。

    考虑优化,因为是祖先中不能存在结束节点,我们给每一个 trie 树上的节点加一个变量表示除去自己外的祖先中是否存在结束节点,这个东西显然从上到下具有传递性,那么可以初步建图了。
    然后看怎么体现一个串的 '?' 的决策情况。发现这个东西不是很好做,因为结束节点不会受到自己影响。那么我们直接新建一个变量好了,表示包含了当前位置的情况下祖先是否有结束节点。

    之后我们还需要解决一个问题,就是一些不同的串可能共用了结束位置,我们显然不能够让他们共用一个变量,因为他们之间也是互相影响的,并且他们还来自不同的 '?' ,不能共用。
    发现这个东西可以直接和上面的情况合并到一个变量上,因为本来我们插入完之后就要新建一个点,那么正好一起用,也不影响答案,把该连的边连上就行了。

    code:

    #include<bits/stdc++.h>
    using namespace std;
    template<class T>inline void init(T&x){
    	x=0;char ch=getchar();bool t=0;
    	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=1;
    	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
    	if(t) x=-x;return;
    }
    #define TR(a) ((a)<<1|1)
    #define FA(a) ((a)<<1)
    const int N=5e5+10;
    const int MAXN=3e6+10;
    int son[MAXN][2];
    char S[N];
    char *s[N];int len[N],id[N];
    int n,cnt=0;
    inline bool cmp(int i,int j){return len[i]<len[j];}
    struct edge{
    	int to,next;
    }a[MAXN];
    int head[MAXN],cur=0,dfn[MAXN],low[MAXN],bel[MAXN],stk[MAXN],top=0,vis[MAXN],I=0,bcc;
    inline void add(int x,int y){a[++cur]=(edge){y,head[x]};head[x]=cur;}
    void tarjan(int u){
    	dfn[u]=low[u]=++I,stk[++top]=u,vis[u]=1;
    	for(int v,i=head[u];i;i=a[i].next){
    		v=a[i].to;
    		if(!dfn[v]) tarjan(v),low[u]=min(low[u],low[v]);
    		else if(vis[v]) low[u]=min(low[u],dfn[v]);
    	}
    	if(dfn[u]==low[u]) {
    		int v;++bcc;
    		do{v=stk[top--];vis[v]=0;bel[v]=bcc;if(bel[v]==bel[v^1]) {puts("NO"),exit(0);}}while(v!=u);
    	}
    	return;
    }
    void Insert(char*s,int len,int Jud){
    	int p=0,las=0;
    	for(int i=1;i<=len;++i) {
    		int c=s[i]-'0';
    		las=p;
    		if(son[p][c]) p=son[p][c];
    		else {
    			son[p][c]=++cnt;
    			if(p) add(TR(p),TR(cnt)),add(FA(cnt),FA(p));
    			p=cnt;
    		}
    	}
    	++cnt;//新建点以防止出现多个串共用了一个点的情况 , 新建的点是要考虑当前点的!
    	add(Jud,FA(p)),add(TR(p),Jud^1);
    	add(TR(p),TR(cnt)),add(FA(cnt),FA(p));
    	add(Jud,TR(cnt)),add(FA(cnt),Jud^1);
    	son[las][(int)(son[las][1]==p)]=cnt;
    }
    int main()
    {
    	init(n);int now=1;cnt=n;// n 个串选择什么
    	for(int i=1;i<=n;++i){
    		scanf("%s",S+now);
    		len[i]=strlen(S+now);
    		s[i]=&S[now-1];now+=len[i];
    		id[i]=i;
    	}sort(id+1,id+1+n,cmp);
    	for(int i=1;i<=n;++i) {
    		int t=id[i];int j=0;
    		for(j=1;j<=len[t];++j) {
    			if(s[t][j]=='?') {// 有问号
    				s[t][j]='0';Insert(s[t],len[t],FA(t));//填 0
    				s[t][j]='1';Insert(s[t],len[t],TR(t));//填 1
    				break;
    			}
    		}
    		if(j>len[t]) {add(TR(t),FA(t));Insert(s[t],len[t],FA(t));}
    	}
    	for(int i=0;i<=cnt;++i) if(!dfn[i]) tarjan(i);
    	puts("YES");
    	return 0;
    }
    
    
  • 相关阅读:
    Spring 系列目录
    Netty 源码 ChannelHandler(四)编解码技术
    Netty 源码 ChannelHandler(三)概述
    Netty 源码 Channel(二)核心类
    Netty 源码 Channel(二)主要类
    NOIP 2017 PJ
    初学 Size Balanced Tree(bzoj3224 tyvj1728 普通平衡树)
    一文读懂后缀自动机 Suffix_Automata
    完美字符子串 单调队列预处理+DP线段树优化
    棋盘覆盖 状压DP+矩阵快速幂
  • 原文地址:https://www.cnblogs.com/NeosKnight/p/10479803.html
Copyright © 2020-2023  润新知