解题报告:洛谷 P6965 [NEERC 2016] Binary Code

QQY 开发了一个抽奖小程序,他选取作业里没人拿下的题来抽奖,于是我抽到了这唯一一道黑题。


这个黑色的 NOI/NOI+/CTSC 看着都很困难。

思路

注意到 n5×105,直接暴力枚举来 2-sat 建边显然会 T 飞。

于是考虑用什么结构能够优化掉枚举。

我们两个 01 字符串之间连边当且仅当一个是另一个的前缀,此处有两种解法:哈希和 Trie树,本题解介绍的是 Trie树 做法。

我们把每个字符串所有可能的情况插入一棵 Trie 中,在树中,两点连边的情况就是一个是另一个祖先。

但是直接暴力做容易被卡成 O(n2),假设拆完点后有 n 个串,我们考虑建立 n 个辅助连边的节点。

我们设 p(i,0/1) 表示第 i 个点,是否在,其辅助节点的编号。

我们希望最后的图可以经过若干中转点相连。

之后为了让我们在各个节点间连边,我们用 ep(i,0/1) 表示 Trie 树上,编号为 i 的点的最后一个 p 值,并让 lst 为上个串的对应节点,x0/1 表示同一个串的两种情况,代码实现中可以同过 ×2(+1) 来实现。

这样我们就需要:

x0plst 表示每个串与刚刚遍历串相连。
px0,0plst 表示各个相同节点但属于不同的串的连成一条链。
px0,0x1 表示到达 x0 的节点都必选 x1 (2-sat)。

可以看看这张图:

image

其中 x10,x20 是同一个串,且是其它下标为 0 的串的前缀,此处下标为 1 的未标出。

以上是前缀部分的统计,我们之后要统计被前缀的。

我们先递归搜索左儿子和右儿子。

之后这样连边:

epfa(lc,1)lst
epfa(rc,1)lst 代表各层之间的连边。

之后我们反着遍历,因为我各层之间的连边是通过最后一个来连接的,所以需要从前往后连,就有。

x0plst
px0,1plst
px0,1x1

这样建复杂度理论上线性的,他本质上是这样的:

我们在 Trie 上,对于不同节点间 只连一条 有向边(虽然最后结果上是无向的),同一节点不同串之间练成一条链。

之后在整张图上跑 tarjan 求强连通分量即可。

闲话

代码最开始 T 了,于是用了快读,结果发现是 cerr 没删导致的...

代码

#include<bits/stdc++.h>
using namespace std;
// #define int long long
#define Air
namespace io {
	class In {
		public:
			template<typename T>
			inline In &operator>>(T &x) {
				x=0;
				bool f=0;
				char c=getchar();
				while(c<'0'||c>'9')
					f|=(c=='-'),c=getchar();
				while(c>='0'&&c<='9')
					x=x*10+c-'0',c=getchar();
				if(c=='.') {
					c=getchar();
					double dot=0.1;
					while(c>='0'&&c<='9')
						x+=(c-'0')*dot,dot*=0.1,c=getchar();
				}
				return (f?x=-x:x),*this;
			}
			inline In &operator>>(char &x) {
				while(isspace(x=getchar()));
				return *this;
			}
			inline In &operator>>(char *x) {
				char c=getchar();
				while(isspace(c)) c=getchar();
				while(!isspace(c)&&~c) *(x++)=c,c=getchar();
				return *x=0,*this;
			}
			inline In &operator>>(string &x) {
				char c=getchar();
				x.clear();
				while(isspace(c)) c=getchar();
				while(!isspace(c)&&~c) x.push_back(c),c=getchar();
				return *this;
			}
			inline In &operator>>(In &in) {
				return in;
			}
	};
	class Out {
		private:
			char buf[35];
			short dot=6,top=0;
		public:
			template<typename T>
			inline Out &operator<<(T x) {
				if(x<0) putchar('-'),x=-x;
				do {
					buf[++top]=x%10,x/=10;
				} while(x);
				while(top) putchar(buf[top--]|'0');
				return *this;
			}
			inline Out &operator<<(char c) {
				return putchar(c),*this;
			}
			inline Out &operator<<(string x) {
				for(auto c:x) putchar(c);
				return *this;
			}
			inline Out &operator<<(char *x) {
				while(*x) putchar(*(x++));
				return *this;
			}
			inline Out &operator<<(const char *x) {
				while(*x) putchar(*(x++));
				return *this;
			}
			inline Out &operator<<(double x) {
				snprintf(buf,sizeof(buf),"%.*lf",dot,x);
				return (*this)<<buf;
			}
			inline Out &operator<<(Out &out) {
				return out;
			}
			inline Out &setdot(const int n) {
				return dot=n,*this;
			}
	};
	In fin;
	Out fout;
	inline Out &setdot(const int n,Out& out=fout) {
		return fout.setdot(n),out;
	}
	inline In &getline(char *x,In& in=fin) {
		char c=getchar();
		while(!(c==' '||!isspace(c))) c=getchar();
		while(c==' '||!isspace(c)) (*x++)=c,c=getchar();
		return *x=0,in;
	}
	inline In &getline(string &x,In& in=fin) {
		char c=getchar();
		x.clear();
		while(!(c==' '||!isspace(c))) c=getchar();
		while(c==' '||!isspace(c)) x.push_back(c),c=getchar();
		return in;
	}
}
using namespace io;
int read(){
	int x;
	fin>>x;
	return x;
}
const int N=5e6+10;
int n;
string s[N],t[N][2];
int a[2][N];
int tot=1;
int ch[N][2];
vector<int>ed[N];
vector<int>e[N];
int ep[N][2];
// namespace Trie{
	void inst(int id,int *arr,int len){
		int x=1;
		// cerr<<id<<' '<<len<<endl;
		for(int i=1;i<=len;i++){
			int c=arr[i];
			if(!ch[x][c]){
				ch[x][c]=++tot;
			}
			x=ch[x][c];
		}
		ed[x].push_back(id);
	}//Trie
// }
int np;
// int p[N][2];
void insert(int x,int y){
	if(!x||!y)return;
	e[x].push_back(y);
}
void build(int now,int fa){
	int lst=ep[fa][0];
	for(auto y:ed[now]){
		++np;
		insert(y,lst);
		insert(np,lst);
		insert(np,y^1);
		lst=np;
	}
	ep[now][0]=lst;
	int lc=ch[now][0],rc=ch[now][1];
	lst=++np;
	if(lc)build(lc,now),insert(lst,ep[lc][1]);
	if(rc)build(rc,now),insert(lst,ep[rc][1]);
	for(int i=ed[now].size()-1;i>=0;i--){
		int y=ed[now][i];
		++np;
		insert(y,lst);
		insert(np,lst);
		insert(np,y^1);
		lst=np;
	}
	ep[now][1]=lst;
}
// namespace get_scc{
	int low[N];
	int dfn[N];
	bool vis[N];
	int scc_cnt=0;
	int tim=0;
	int col[N];
	stack<int>st;
	void tarjan(int now){
	    low[now]=dfn[now]=++tim;
	    st.push(now);
	    vis[now]=1;
	    for(auto y:e[now]){
	        if(dfn[y]&&vis[y]){
	            low[now]=min(low[now],dfn[y]);
	        }
	        if(!dfn[y]){
	            tarjan(y);
	            low[now]=min(low[now],low[y]);
	        }
	    }
	    if(dfn[now]==low[now]){
	        scc_cnt++;
	        while(st.top()!=now){
	            int id=st.top();
	            st.pop();
	            vis[id]=0;
	            col[id]=scc_cnt;
	            // quan[idx]+=a[id];
	        }
	        // int id=st.top();
	        st.pop();
	        vis[now]=0;
	        col[now]=scc_cnt;
	        // quan[idx]+=a[now];
	    }
	}	
// }
// using namespace Trie;
signed main(){
#ifndef Air
    freopen(".in","r",stdin);
	freopen(".out","w",stdout);
#endif
	n=read();
	np=n*2+1;//new node
	for(int i=1;i<=n;i++){
		fin>>s[i];
		t[i][0]=t[i][1]=s[i];
		bool flag=0;
		for(int j=0;j<s[i].size();j++){
			if(s[i][j]=='?'){
				a[0][j+1]=0;
				a[1][j+1]=1;
				t[i][0][j]='0';
				t[i][1][j]='1';
				flag=1;
			}
			else{
				a[0][j+1]=s[i][j]-'0';
				a[1][j+1]=s[i][j]-'0';
			}
		}
		inst(i*2,a[0],s[i].size());
		inst(i*2+1,a[1],s[i].size());
		// for(int j=1;j<=s[i].size();j++){
			// cout<<a[1][j]<<s[i].size();
		// }
		// cout<<endl;
		if(!flag){
			e[i*2].push_back(i*2+1);
		}
	}
	build(1,0);
	// using namespace get_scc;
	// tarjan(1);
    for(int i=1;i<=np;i++){
    	if(!dfn[i]){
    		// cerr<<i<<endl;
    		tarjan(i);
    	}
    }
    for(int i=1;i<=n;i++){
    	if(col[i*2]==col[i*2+1]){
    		puts("NO");
    		return 0;
    	}
    }
    puts("YES");
	for(int i=1;i<=n;i++){
		if(col[i*2]<col[i*2+1]){
    		// puts("NO");
    		fout<<t[i][0]<<'\n';
    		// return 0;
    	}
    	else{
    		fout<<t[i][1]<<'\n';
    	}
	}   
    return 0;
}
posted @   Air2011  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示