题意:
小S打算送给小M一棵nn个点的圣诞树,点从到nn标号,他打算给树上挂上个礼物,每个礼物在树上的某个点上,礼物可以重叠。
小M给了小S qq个限制,其中第个形如“第aiai个礼物和第bibi个礼物在树上的路径经过了点cici”。
构造出符合小M条件的挂礼物方案。
题解:
这题一开始想到2−SAT2−SAT,记fi,jfi,j表示第ii个点放第个礼物的boolbool变量,不过发现这种方法限制不能完全体现出来,自然不能找到一种合法的方案(比如如果所有的fj,pfj,p都不选,那么fi,pfi,p必须选)。
然后因为是树上,有一个tricktrick ,记fi,jfi,j 为第ii个点及其子树放第个点的boolbool变量, 具体我们用ai,jai,j表示jj在的子树中,bi,jbi,j表示jj不在的子树中。 这样不仅能够体现出所有限制而且能够很方便的输出方案(这时候根节点为truetrue,且具体方案为深度最大的那个合法点)。
我们先连边:
- b1,i→a1,ib1,i→a1,i
- ai,j→afai,jai,j→afai,j
- bi,j→bsoni,jbi,j→bsoni,j
- ai,j→bk,jai,j→bk,j (如果ii的子树与的子树交集为空)
对于每一个限制,连边: - ai,a/b→bj,b/aai,a/b→bj,b/a (ii,树上父亲为cc)
然后跑2−SAT2−SAT即可。 时间复杂度为O(n+m)=O(n2m+qn2)O(n+m)=O(n2m+qn2)
其实复杂度可以优化,每个点维护儿子的前缀后缀即可。 不过太麻烦就不写了。
#include <bits/stdc++.h>
using namespace std;
inline int rd() {
char ch=getchar(); int i=0,f=1;
while(!isdigit(ch)) {if(ch=='-')f=-1; ch=getchar();}
while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=getchar();}
return i*f;
}
const int N=2e2+55,M=N*N*2;
int n,m,q,A[N][N],B[N][N],d[N],fa[N],tot,ins[M],sta[M],top,dfn[M],low[M],bl[M],scc,ind,pos[M];
vector <int> son[N];
vector <int> e[N];
vector <int> e2[M];
inline int lca(int x,int y) {while(x!=y) (d[x]>d[y])?(x=fa[x]):(y=fa[y]);}
inline bool anc(int x,int f) {return (x==f)?true:(x?anc(fa[x],f):false);}
inline void dfs(int x,int f) {
fa[x]=f; d[x]=d[f]+1;
for(auto v:e[x]) if(v!=f) son[x].push_back(v), dfs(v,x);
}
inline void dfs(int x) {
sta[++top]=x; ins[x]=1; dfn[x]=low[x]=++ind;
for(auto v:e2[x]) {
if(!dfn[v]) dfs(v), low[x]=min(low[x],low[v]);
else if(ins[v]) low[x]=min(low[x],dfn[v]);
}
if(low[x]==dfn[x]) {
++scc; int u;
do {
u=sta[top--];
ins[u]=0; bl[u]=scc;
} while(u!=x);
}
}
int main() {
n=rd(), m=rd(), q=rd();
for(int i=1;i<n;i++) {
int x=rd(), y=rd();
e[x].push_back(y);
e[y].push_back(x);
}
dfs(1,0);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
A[i][j]=++tot, B[i][j]=++tot;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
if(fa[i]) e2[A[i][j]].push_back(A[fa[i]][j]);
for(auto v:son[i]) e2[B[i][j]].push_back(B[v][j]);
}
for(int j=1;j<=m;j++) e2[B[1][j]].push_back(A[1][j]);
for(int i=1;i<=n;i++)
for(int k=1;k<=n;k++)
if(i!=k && !anc(i,k) && !anc(k,i))
for(int j=1;j<=m;j++) e2[A[i][j]].push_back(B[k][j]);
for(int i=1;i<=q;i++) {
int a=rd(), b=rd(), c=rd();
for(auto v:son[c]) {
e2[A[v][a]].push_back(B[v][b]);
e2[A[v][b]].push_back(B[v][a]);
}
if(fa[c]) e2[B[c][a]].push_back(A[c][b]), e2[B[c][b]].push_back(A[c][a]);
}
for(int i=1;i<=tot;i++) if(!dfn[i]) dfs(i);
for(int j=1;j<=m;j++)
for(int i=1;i<=n;i++)
if(bl[A[i][j]]<bl[B[i][j]])
pos[j]=(d[pos[j]]<d[i])?i:pos[j];
for(int i=1;i<=m;i++) printf("%d ",pos[i]);
}