[CF1007D] Ants (tree chain subdivision + 2-SAT)

Keywords: data structure Link/cut tree CodeForces 2-SAT

Click here to see the problem

  • There is a tree with \ (n \) points.
  • You need to perform \ (m \) coloring operations. Each operation gives two point pairs \ ((x_1,y_1) \) and \ ((x_2,y_2) \), and you need to select a point pair to color all the edges in the path on the corresponding tree. It is required that the same edge cannot be dyed repeatedly.
  • Find a legal solution.
  • \(2\le n\le10^5\),\(1\le m\le10^4\)


Each dyeing operation needs to select exactly one of the two point pairs, which is obviously a classical 2-SAT problem.

Mapping is to consider that point pairs with intersection of two paths cannot be selected at the same time.

Tree chain subdivision optimization drawing

Mark each edge on the deeper of the two endpoints.

For each point pair \ ((x,y) \), you need to mark all edges on its path, which is equivalent to marking all points on the \ (x,y \) path except \ (\ operatorname{LCA}(x,y) \).

The marking process can be realized by tree chain segmentation + line segment tree optimization.

Then, for the markers on the nodes with ancestor descendant relationship on the segment tree (including on the same node), they cannot be selected at the same time.

If only the marks on the same node cannot be selected at the same time, there is a classic prefix optimization mapping: (connect edges from all \ (1 \) to all \ (0 \) except their corresponding points, and \ (A,B \) is the auxiliary point)

In order to make the markers on the nodes with ancestor descendant relationship cannot be selected at the same time, because we only need to know the two auxiliary points added in the last mapping in the process of prefix optimization mapping, we can actually make the two child nodes continue to build the map on the basis of the last two auxiliary points of prefix optimization mapping from the root node of the line segment tree to the current point. It is easy to find that their edges do not interfere with each other.

Code: \ (O(n\log^2n) \)

#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 100000
#define M 10000
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
using namespace std;
int n,m,ee,lnk[N+5];struct edge {int to,nxt;}e[N<<1];
namespace FastIO
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
	Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('\n');}
}using namespace FastIO;
namespace G
	#define PS M*1200
	#define ES M*1800
	int ee,lnk[PS+5];edge e[ES+5];I void Add(CI x,CI y) {add(x,y);}
	int d,dfn[PS+5],low[PS+5],T,S[PS+5],IS[PS+5],ct,bl[PS+5];I void dfs(CI x)//Tarjan
		dfn[x]=low[x]=++d,IS[S[++T]=x]=1;for(RI i=lnk[x],y;i;i=e[i].nxt)
		if(dfn[x]==low[x]) {++ct;W(bl[S[T]]=ct,IS[S[T]]=0,S[T--]^x);}
	I void Solve()
		RI i;for(i=1;i<=2*m;++i) !dfn[i]&&(dfs(i),0);
		for(i=1;i<=m;++i) if(bl[i]==bl[i+m]) return (void)puts("NO");puts("YES");//unsolvable
		for(i=1;i<=m;++i) puts(bl[i]<bl[i+m]?"1":"2");
namespace T
	int d,D[N+5],dfn[N+5],sz[N+5],f[N+5],g[N+5],tp[N+5];
	int ct;I void U(int& k,CI x)//Based on the auxiliary points k-1 and K, prefix optimization is used to build the map
		RI y=x<=m?x+m:x-m,o=2*m+(ct+=2);G::Add(x,o-1),G::Add(o,y);
		if(k) G::Add(k-1,o-1),G::Add(k-1,y),G::Add(x,k),G::Add(o,k);k=o;
	class SegmentTree
			#define PT CI l=1,CI r=n,CI rt=1
			#define LT l,mid,rt<<1
			#define RT mid+1,r,rt<<1|1
			vector<int> G[N<<2];
			I void A(CI L,CI R,CI id,PT)//Interval marking
				if(L<=l&&r<=R) return (void)G[rt].push_back(id);RI mid=l+r>>1;L<=mid&&(A(L,R,id,LT),0),R>mid&&(A(L,R,id,RT),0);
			I void Walk(RI k=0,PT)//Optimized mapping
				for(vector<int>::iterator it=G[rt].begin();it!=G[rt].end();++it) U(k,*it);//Mark the current point
				if(l==r) return;RI mid=l+r>>1;Walk(k,LT),Walk(k,RT);//The two child nodes continue to build the graph based on the current point and the final auxiliary point
	I void dfs1(CI x)//First dfs of tree section
		sz[x]=1;for(RI i=lnk[x],y;i;i=e[i].nxt) (y=e[i].to)^f[x]&&(D[y]=D[f[y]=x]+1,dfs1(y),sz[x]+=sz[y],sz[y]>sz[g[x]]&&(g[x]=y));
	I void dfs2(CI x,CI t)//Second dfs of tree section
		if(dfn[x]=++d,tp[x]=t,g[x]) {dfs2(g[x],t);for(RI i=lnk[x],y;i;i=e[i].nxt) (y=e[i].to)^f[x]&&y^g[x]&&(dfs2(y,y),0);}
	I void A(RI x,RI y,CI id)//The tree section marks the path on the tree (except LCA)
		W(tp[x]^tp[y]) D[tp[x]]<D[tp[y]]&&(swap(x,y),0),S.A(dfn[tp[x]],dfn[x],id),x=f[tp[x]];D[x]>D[y]&&(swap(x,y),0),x^y&&(S.A(dfn[x]+1,dfn[y],id),0);
int main()
	RI i,x,y;for(read(n),i=1;i^n;++i) read(x,y),add(x,y),add(y,x);T::dfs1(1),T::dfs2(1,1);
	RI a,b,c,d;for(read(m),i=1;i<=m;++i) read(a,b,c,d),T::A(a,b,i),T::A(c,d,i+m);return T::S.Walk(),G::Solve(),0;

Posted by Andrei on Thu, 04 Nov 2021 10:57:34 -0700