N個命題至多成立一個的建圖
建立n個前綴命題i,pre[i]表示前i個命題存在一個成立
link(u,v)表示加邊 u>v 和 ~v > ~u
則只需要像這樣建邊即可:
for(int i = 0; i < n; i++) {
if(i) {
link(pre[i - 1], pre[i]);
link(i, pre[i - 1] ^ 1);
}
link(i, pre[i]);
}
【題目】
給定一些二進制編碼,每個編碼至多有一個位置不知道是什麼。問是否存在一種補全編碼方式,使得沒有任何一個編碼是另一個編碼的前綴。
題目鏈接:https://codeforces.com/gym/101190
【題解】
將所有編碼串插入字典樹中,其中不帶問號的串節點必選,帶問號的補爲0和1分別插入的串二選一。將選0和選1分別設置爲命題的真假。
問題轉化爲在Trie樹中任意節點到根節點的鏈上至多隻有一個節點被選中。
因爲一個節點上可能有多個串的結尾,還要保證每個節點上多個串最多隻選一個
代碼:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 4e6+5;
vector<int> G[maxn];
inline void add(int u,int v){ G[u].push_back(v); }
inline void link(int u,int v){ add(u, v), add(v^1, u^1);}
int low[maxn], dfn[maxn], stk[maxn], belong[maxn];
bool instack[maxn];
int idx, top, scc;
void tarjan(int u){
low[u] = dfn[u] = ++idx;
stk[top++] = u;
instack[u] = true;
for(int &v:G[u]){
if(!dfn[v]){
tarjan(v);
if(low[u] > low[v]) low[u] = low[v];
}else if(instack[v] && low[u] > dfn[v])
low[u] = dfn[v];
}
if(low[u] == dfn[u]){
scc++;
for(;;){
int v = stk[--top];
instack[v] = false;
belong[v] = scc;
if(v == u) break;
}
}
}
int ch[maxn][2], tot;
int fa[maxn];
inline int insert(const char *s){
int now = 0;
for(int i = 0; s[i]; i++){
if(!ch[now][s[i]-'0']) fa[ch[now][s[i]-'0'] = ++tot] = now;
now = ch[now][s[i]-'0'];
}
return now;
}
int n;
string s[maxn];
int wh[maxn], pos[maxn];
vector<int> loc[maxn];
int main() {
#ifndef LOCAL
freopen("binary.in", "r", stdin);
freopen("binary.out", "w", stdout);
#endif // LOCAL
cin>>n;
for(int i = 1; i <= n; i ++){
cin>>s[i];
wh[i] = -1;
for(int j = 0; j < s[i].size(); j++)
if(s[i][j] == '?') wh[i] = j;
if(wh[i] == -1){
pos[i<<1] = pos[i<<1|1] = insert(s[i].c_str());
add(i<<1|1,i<<1);//f -> t
}else{
s[i][wh[i]] = '0'; pos[i<<1] = insert(s[i].c_str());
s[i][wh[i]] = '1'; pos[i<<1|1] = insert(s[i].c_str());
}
}
int S = (n<<1|1)+1;
for(int i = 1; i <= tot; i++)
link(S + (fa[i]<<1), S + (i<<1));
for(int i = 1; i <= n; i++){
if(wh[i] == -1){
loc[pos[i<<1]].push_back(i<<1);
link(i<<1, S+(fa[pos[i<<1]]<<1|1));
link(i<<1, S+(pos[i<<1]<<1));
}else{
loc[pos[i<<1]].push_back(i<<1);
loc[pos[i<<1|1]].push_back(i<<1|1);
link(i<<1, S+(fa[pos[i<<1]]<<1|1));
link(i<<1, S+(pos[i<<1]<<1));
link(i<<1|1, S+(fa[pos[i<<1|1]]<<1|1));
link(i<<1|1, S+(pos[i<<1|1]<<1));
}
}
S += (tot<<1|1) + 1;
for(int i = 1; i <= tot; i++){
if(!loc[i].size()) continue;
for(int j = 0; j < loc[i].size(); j++){
if(j){
link(S+(j-1<<1), S+(j<<1));
link(loc[i][j], S + ((j-1<<1)|1));
}
link(loc[i][j], S + (j<<1));
}
S += (loc[i].size()<<1);
}
for(int i = 0; i <= S; i++)
if(!dfn[i]) tarjan(i);
for(int i = 1; i <= n; i++)
if(belong[i<<1] == belong[i<<1|1]) return puts("NO"),0;
puts("YES");
for(int i = 1; i <= n; i++){
if(wh[i] != -1)
s[i][wh[i]] = (belong[i<<1]<belong[i<<1|1]?'0':'1');
cout<<s[i]<<endl;
}
return 0;
}