题意:每次优先按权重最小的边dfs,不重复搜一个节点,问从那些点作为根节点搜索得到的路径与最小生成树一样?
思路见官方题解 Codeforces Round #808 Editorial - Codeforces
主要是先得到最小生成树,每条多余的边能排除一些最小生成树的点.比如上图多余的边是(u,v),就能把u,v以下的点都排除了.
但是怎么在O(n)复杂度得到这些点?,就要用到差分思想,参考树上差分详解 - TEoS - 博客园
差分思想就是用一些特殊点代表一种累计效果.最后再求一次前缀和. 不同情况下细节略有不同.
改题中还需要求lca
import java.util.*;
import java.io.*;
public class Main {
public static void main(String args[]) {new Main().run();}
FastReader in = new FastReader();
PrintWriter out = new PrintWriter(System.out);
void run() {
work();
out.flush();
}
long mod=998244353;
long gcd(long a,long b) {
return a==0?b:gcd(b%a,a);
}
final long inf=Long.MAX_VALUE/3;
ArrayList<Integer>[] graph;
int[] id;
int[][] dp;
int[] deep;
int[] rec;
void work(){
int n=ni(),m=ni();
graph=new ArrayList[n];
deep=new int[n];
dp=new int[n][20];
id=new int[n];
for(int i=0;i<n;i++){
graph[i]=new ArrayList<>();
id[i]=i;
}
rec=new int[n];//差分
int[][] edge=new int[m-n+1][];
for(int i=0,j=0;i<m;i++){
int s=ni()-1,e=ni()-1;
if(union(s,e)){
edge[j++]=new int[]{s,e};
}else{
graph[s].add(e);
graph[e].add(s);
}
}
dfs(0,-1);
for(int i=0;i<edge.length;i++){
int n1=edge[i][0],n2=edge[i][1];
int lca = lca(n1, n2);
if(lca==n1){
int cur=n2;
for(int j=19;j>=0;j--){
if(dp[cur][j]!=-1&&deep[dp[cur][j]]>deep[n1]){
cur=dp[cur][j];
}
}
rec[cur]++;
rec[n2]--;
}else if(lca==n2){
int cur=n1;
for(int j=19;j>=0;j--){
if(dp[cur][j]!=-1&&deep[dp[cur][j]]>deep[n2]){
cur=dp[cur][j];
}
}
rec[cur]++;
rec[n1]--;
}else{
rec[0]++;
rec[n1]--;
rec[n2]--;
}
}
dfs2(0,-1);
StringBuilder sb=new StringBuilder();
for(int i=0;i<n;i++){
if(rec[i]>0)sb.append(0);
else sb.append(1);
}
out.println(sb.toString());
}
private void dfs2(int node, int pre) {
if(pre!=-1){
rec[node]+=rec[pre];
}
for(int nn:graph[node]){
if(nn!=pre){
dfs2(nn,node);
}
}
}
private void dfs(int node, int pre) {
dp[node][0]=pre;
if(pre!=-1){
deep[node]=deep[pre]+1;
}
for(int i=1;i<20&&dp[node][i-1]!=-1;i++){
dp[node][i]=dp[dp[node][i-1]][i-1];
}
for(int nn:graph[node]){
if(nn!=pre){
dfs(nn,node);
}
}
}
private int lca(int n1,int n2){
int d1=deep[n1],d2=deep[n2];
int d=Math.min(d1,d2);
int cur1=n1,cur2=n2;
for(int i=19;i>=0;i--){
if(dp[cur1][i]!=-1&&deep[dp[cur1][i]]>=d){
cur1=dp[cur1][i];
}
}
for(int i=19;i>=0;i--){
if(dp[cur2][i]!=-1&&deep[dp[cur2][i]]>=d){
cur2=dp[cur2][i];
}
}
if(cur1==cur2)return cur1;
for(int i=19;i>=0;i--){
if(dp[cur1][i]!=dp[cur2][i]){
cur1=dp[cur1][i];
cur2=dp[cur2][i];
}
}
return dp[cur1][0];
}
boolean union(int q,int p){
int proot = find(p);
int qroot = find(q);
if(proot==qroot){
return true;
}
id[proot]=qroot;
return false;
}
int find(int q){
if(q!=id[q]){
id[q]=find(id[q]);
}
return id[q];
}
@SuppressWarnings("unused")
private ArrayList<Integer>[] ng(int n, int m) {
ArrayList<Integer>[] graph=(ArrayList<Integer>[])new ArrayList[n];
for(int i=0;i<n;i++) {
graph[i]=new ArrayList<>();
}
for(int i=1;i<=m;i++) {
int s=in.nextInt()-1,e=in.nextInt()-1;
graph[s].add(e);
graph[e].add(s);
}
return graph;
}
private ArrayList<long[]>[] ngw(int n, int m) {
ArrayList<long[]>[] graph=(ArrayList<long[]>[])new ArrayList[n];
for(int i=0;i<n;i++) {
graph[i]=new ArrayList<>();
}
for(int i=1;i<=m;i++) {
long s=in.nextLong()-1,e=in.nextLong()-1,w=in.nextLong();
graph[(int)s].add(new long[] {e,w});
graph[(int)e].add(new long[] {s,w});
}
return graph;
}
private int ni() {
return in.nextInt();
}
private long nl() {
return in.nextLong();
}
private double nd() {
return in.nextDouble();
}
private String ns() {
return in.next();
}
private long[] na(int n) {
long[] A=new long[n];
for(int i=0;i<n;i++) {
A[i]=in.nextLong();
}
return A;
}
private int[] nia(int n) {
int[] A=new int[n];
for(int i=0;i<n;i++) {
A[i]=in.nextInt();
}
return A;
}
}
class FastReader
{
BufferedReader br;
StringTokenizer st;
InputStreamReader input;//no buffer
public FastReader()
{
br=new BufferedReader(new InputStreamReader(System.in));
}
public FastReader(boolean isBuffer)
{
if(!isBuffer){
input=new InputStreamReader(System.in);
}else{
br=new BufferedReader(new InputStreamReader(System.in));
}
}
public boolean hasNext(){
try{
String s=br.readLine();
if(s==null){
return false;
}
st=new StringTokenizer(s);
}catch(IOException e){
e.printStackTrace();
}
return true;
}
public String next()
{
if(input!=null){
try {
StringBuilder sb=new StringBuilder();
int ch=input.read();
while(ch=='\n'||ch=='\r'||ch==32){
ch=input.read();
}
while(ch!='\n'&&ch!='\r'&&ch!=32){
sb.append((char)ch);
ch=input.read();
}
return sb.toString();
}catch (Exception e){
e.printStackTrace();
}
}
while(st==null || !st.hasMoreElements())//回车,空行情况
{
try {
st = new StringTokenizer(br.readLine());
} catch (IOException e) {
e.printStackTrace();
}
}
return st.nextToken();
}
public int nextInt()
{
return (int)nextLong();
}
public long nextLong() {
try {
if(input!=null){
long ret=0;
int b=input.read();
while(b<'0'||b>'9'){
b=input.read();
}
while(b>='0'&&b<='9'){
ret=ret*10+(b-'0');
b=input.read();
}
return ret;
}
}catch (Exception e){
e.printStackTrace();
}
return Long.parseLong(next());
}
public double nextDouble()
{
return Double.parseDouble(next());
}
}