Problem A. [CF1198C] Matching vs Independent Set
Description
给定一张 \(3n\) 个点 \(m\) 条边的无向图,在以下任务中选择其一完成:
- 找到一个大小为 \(n\) 的独立集;
- 找到一个大小为 \(n\) 的匹配。
\(n \leq 10^5,m \leq 5\times 10^5\)。
Solution
猜测一定有解。
\(3n\) 个点这个限制很奇怪。注意到独立集最多有 \(n\) 个点,匹配最多占据 \(2n\) 个点,加起来正好是 \(3n\)。
首先尝试构造匹配。枚举每一条边,如果其两端都没有标记,就将其加入到匹配中,并标记两端。
如果匹配大小已经达到 \(n\),那么就结束了。
否则,剩下未被标记的节点数量 \(>n\),且一定是一个独立集。考虑反证,如果剩下的点集中存在一条连边,那么其一定会被加入到匹配中。
Problem B. [CF618F] Double Knapsack
Description
给定序列 \(A,B\),分别找出 \(A,B\) 的一个子集 \(S,T\),使得 \(Sum(S)=Sum(T)\)。
\(n \leq 10^6,1 \leq A_i,B_i \leq n\)。
Solution
子集数量为 \(O(2^n)\),但是和不同的子集数量只有 \(O(n^2)\),看上去很不平衡。
转化一下题意:重排 \(A,B\) 并找出和相等的两个子段。
大胆猜测,对于任意一个 \(A,B\),必定存在满足条件的子段。
对 \(A,B\) 分别求前缀和数组 \(Sa,Sb\)。
首先如果 \(Sa_n>Sb_n\),那么交换 \(A,B\) 与 \(Sa,Sb\)。现在 \(Sa_n \leq Sb_n\)。
每一个 \(i\) 都能找到一个 \(j\),使得 \(Sb_j\geq Sb_i\)。
那么我们对于每一个 \(i\),找到一个最小的 \(j\),使得 \(Sb_j\geq Sa_i\)。由于序列中每个数字都在 \([1,n]\) 之间,那么 \(Sb_j-Sa_i \in [0,n-1]\)。
\(i\) 可以取遍 \([0,n]\),共有 \(n+1\) 个位置,但 \(Sb_j-Sa_i\) 只有 \(n\) 种取值。根据鸽巢原理,一定有两个差会相等。
假如这两个 \(i\) 分别为 \(i_1,i_2\),那么有 \(Sa_{i_1}-Sb_{j_1}=Sa_{i_2}-Sb_{j_2} \Leftrightarrow Sa_{i_1}-Sa_{i_2}=Sb_{j_1}-Sb_{j_2}\)。
于是我们找到了 \(A[i_1,i_2]\) 与 \(B[j_1,j_2]\) 两个合法的子段。
用证明中的方法构造即可。
Problem C. [CF1325F] Ehab's Last Theorem
Description
给定一张 \(n\) 个点,\(m\) 条边的简单无向连通图。设 \(k=\lceil \sqrt{n}\rceil\)从以下任务中选择其一完成:
- 找到一个长度 \(\geq k\) 的环;
- 找到一个大小 \(=k\) 的独立集。
\(n,m \leq 2\times 10^5\)。
Solution
猜测一定有解。
首先构造环。我们把问题放到 dfs 树上来考虑。
一个重要性质:无向图的 dfs 树上没有横叉边。那么我们只需考虑返祖边。
假如我们遍历到了 \(x\),一条返祖边为 \((x,y)\)。如果 \(dep_x-dep_y+1\geq k\), \((y \rightarrow x \rightarrow y)\) 就是一个合法的环。
如果没有找到合法的环,那么对于任意的 \(x\) 以及其返祖边连接的 \(y\),一定有 \(dep_x-dep_y+1 \leq k-1\)。由于没有重边,那么 \(x\) 的返祖边数量不超过 \(k-3\)(考虑从大小为 \(k-1\) 的环中除去自己和父亲)。
我们从叶子开始,从底至顶构造独立集。如果这个点没有被标记,那么将其加入独立集,并标记自己以及相邻的节点。可以发现,这样构造每次加入的点一定没有未被标记的儿子。
那么我们每次最多标记 \(k-1=\lceil \sqrt{n}\rceil-1\leq \lfloor\sqrt{n} \rfloor\) 个节点。
如果等号成立,那么结束;否则一定还剩下至少一个点,将其加入独立集即可。
void dfs(int x,int pr){dep[x]=dep[pr]+1;s[++cnt]=x;for(int i=head[x];i;i=edge[i].nxt){int t=edge[i].to;if(!dep[t]) dfs(t,x);else if(dep[x]-dep[t]+1>=k){printf("2\n%d\n",dep[x]-dep[t]+1);for(int j=dep[t];j<=dep[x];j++)printf("%d ",s[j]);puts(""); exit(0);}}if(!vis[x]){vis[x]=1;for(int i=head[x];i;i=edge[i].nxt)vis[edge[i].to]=1;S.push_back(x);}cnt--;
}
Problem D. [CF1470D] Strange Housing
Description
给定一个 \(n\)个节点,\(m\) 条无向边的图,现在你要给一些点染色,使得:
- 一条边所连接的两个点不能都被染色。
- 在所有连接两个不被染色的点的边都被删除的情况下,这个图满足任意两个点互相可达。
\(n,m \leq 3\times 10^5\)。
Solution
题意转化为:将点集拆为两个集合 \(S,T\),满足 \(S\) 为一个独立集,且 \(T\) 中的点均与 \(S\) 中的点有连边。
猜测如果图联通,一定有解,否则无解。
我们从任意一点出发,类似于二分图染色地做 dfs。选入 \(S\) 标记为 \(1\),否则为 \(0\)。
如果点 \(x\) 标记为 \(1\),那么 \(x\) 的所有邻居都必须标记为 \(0\)。我们先把 \(x\) 未被标记的邻居拿出来,先一块标记为 \(0\),再继续进行 dfs。
如果点 \(x\) 标记为 \(0\),那么我们遍历 \(x\) 的所有邻居 \(y\),如果 \(y\) 未被标记,那么把 \(y\) 标记为 \(1\),进入 \(y\) 继续 dfs。
首先这样保证了每个标记为 \(0\) 的点都至少有一个标记为 \(1\) 的邻居。当 \(x\) 被标记为 \(1\) 时,它会立即把所有邻居标记为 \(0\),又保证了 \(S\) 为独立集。
void dfs(int x){if(col[x]==1){S.push_back(x);vector<int> T;for(int i=head[x];i;i=edge[i].nxt){int t=edge[i].to;if(col[t]==-1) T.push_back(t),col[t]=0;}for(int i:T) dfs(i);}else{for(int i=head[x];i;i=edge[i].nxt){int t=edge[i].to;if(col[t]==-1){col[t]=1;dfs(t);}}}
}