P6378 [PA2010] Riddle-2sat优化建图
P6378 [PA2010] Riddle-2sat优化建图
\(n\) 个点 \(m\) 条边的无向图被分成 \(k\) 个部分。每个部分包含一些点。
请选择一些关键点,使得每个部分恰有一个关键点,且每条边至少有一个端点是关键点。
\(1\leq n,m\leq 10^6\)
边的限制
用 \(n\) 个变量 \(x_1\dots x_n\) ,其中 \(x_i\) 表示命题:“ \(i\) 是关键点”,与此同时也需要另外 \(n\) 个变量 \(\neg x_1,\dots,\neg x_n\) ,自然地 \(\neg x_i\) 表示命题”\(i\) 不是关键点“。
那么对于一条边的约束,就变成形如:
\[x_i\lor x_j=(\neg x_i\to x_j)\land (\neg x_j\to x_i)
\]
的约束,把 \(x_1\dots,x_n,\neg x_1,\dots,\neg x_n\) 看成 \(2n\) 个结点,问题约束就变成了要选择一些点,使得如果选了点 \(x\) ,那么所有 \(x\) 能到达的点也要选,以及很明显不能同时选择 \(x_i\) 和 \(\neg x_i(\forall i=1,\dots,n)\)。
每个部分的限制
每个部分 \(S\) 中的元素至多选择一个,即如果选择了 \(x\) ,那么要向所有\(\neg y(y\in S,y\neq x)\) 连边,暴力连边是 \(O(n^2)\) 的显然不行。
但这种建图其实也很常见,每次给集合内除了自己之外的点连边,假设 \(S\) 中的点有序,排成 \(a_1,\dots,a_w\) ,那么对于每个 \(a_i\) 只要给前 \(i-1\) 个点和 \(i+1\) 往后的 \(n-i\) 个点连边,所以可以额外创建 \(w\) 个点表示前缀,\(w\) 个点表示后缀。
建图代码:
rep(tc,1,k){
int w,c;cin>>w;
rep(i,1,w)cin>>a[i];
rep(i,1,w)pre[i]=++tot;
rep(i,1,w)suf[i]=++tot;
rep(i,1,w){
addEdge(pre[i],a[i]+n);
addEdge(suf[i],a[i]+n);
if(i>1){
addEdge(pre[i],pre[i-1]);
addEdge(a[i],pre[i-1]);
}
if(i<w){
addEdge(suf[i],suf[i+1]);
addEdge(a[i],suf[i+1]);
}
}
}
完整代码:
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
const int N=4e6+5;
vector<vector<int>> G,H;
int n,m,k,tot,pre[N],suf[N],a[N];
int bl[N],scc,dfscnt,dfn[N],low[N];
bool in_stack[N];
stack<int> S;
void tarjan(int x){
low[x]=dfn[x]=++dfscnt;
S.push(x);in_stack[x]=1;
for(auto to:G[x]){
if(!dfn[to]){
tarjan(to);
low[x]=min(low[x],low[to]);
}else if(in_stack[to])
low[x]=min(low[x],dfn[to]);
}
if(low[x]==dfn[x]){
++scc;
bool tag=0;
while(1){
bl[S.top()]=scc;
if(S.top()==x)tag=1;
in_stack[S.top()]=0;
S.pop();
if(tag)break;
}
}
}
void addEdge(int x,int y){G[x].push_back(y);}
int main(){
fastio;
cin>>n>>m>>k;
G=vector<vector<int>>(4*n+5);
rep(i,1,m){
int x,y;cin>>x>>y;
addEdge(x+n,y);
addEdge(y+n,x);
}
tot=2*n;
rep(tc,1,k){
int w,c;cin>>w;
rep(i,1,w)cin>>a[i];
rep(i,1,w)pre[i]=++tot;
rep(i,1,w)suf[i]=++tot;
rep(i,1,w){
addEdge(pre[i],a[i]+n);
addEdge(suf[i],a[i]+n);
if(i>1){
addEdge(pre[i],pre[i-1]);
addEdge(a[i],pre[i-1]);
}
if(i<w){
addEdge(suf[i],suf[i+1]);
addEdge(a[i],suf[i+1]);
}
}
}
rep(i,1,tot)if(!bl[i])tarjan(i);
bool bad=0;
rep(i,1,n)if(bl[i]==bl[i+n])bad=1;
if(bad)cout<<"NIE";
else cout<<"TAK";
return 0;
}