zoukankan      html  css  js  c++  java
  • [HNOI2019]校园旅行

    https://www.luogu.org/problemnew/show/P5292

    题目描述

    某学校的每个建筑都有一个独特的编号。一天你在校园里无聊,决定在校园内随意地漫步。

    你已经在校园里呆过一段时间,对校园内每个建筑的编号非常熟悉,于是你情不自禁的把周围每个建筑的编号都记了下来——但其实你没有真的记下来,而是把每个建筑的编号除以 2 取余数得到 0 或 1,作为该建筑的标记,多个建筑物的标记连在一起形成一个 01 串。

    你对这个串很感兴趣,尤其是对于这个串是回文串的情况,于是你决定研究这个问题。

    学校可以看成一张图,建筑是图中的顶点,而某些顶点之间存在无向边。对于每个顶点我们有一个标记(0 或者 1)。每次你会选择图中两个顶点,你想知道这两个顶点之间是否存在一条路径使得路上经过的点的标记形成一个回文串。

    一个回文串是一个字符串使得它逆序之后形成的字符串和它自己相同,比如“010”,“1001”都是回文串,而“01”,“110”不是。注意长度为 1 的串总是回文串,因此如果询问的两个顶点相同,这样的路径总是存在。此外注意,经过的路径不一定为简单路径,也就是说每条边每个顶点都可以经过任 意多次。

    题解

    非常巧妙的一道题。

    先考虑朴素的做法,记(dp[l][r])表示(l)(r)能否构成一个回文串,这个可以用(BFS)扩展。

    分析一下复杂度,发现它是(m^2)的。

    考虑到边数很多而点数很少,所以考虑把不必要的边去掉。

    考虑连接颜色相同的边构成的联通块,然后有结论,如果这是一个二分图,那么我们只需要保留其中的一颗生成树,如果不是,那么给其中的一个点加上一个自环就好了。

    那么对于连接两种颜色的边也一样,因为它本身是一个二分图,所以就保留一个生成树就好了。

    至于原因,考虑如果是一个二分图,从((x,y))扩展到((x',y')),那么(x)走的步数和(y)走的步数的奇偶性是一样的,所以我们需要证明当走的步数足够大的时候,只要奇偶性不变,从(x)一定能够走到(x'),

    因为是二分图,我们多的边可以来回走就好了。

    如果不是二分图,我们可以通过自环来调整奇偶性。

    这样我们就把边数调整到了(O(n))级别。

    代码

    #include<bits/stdc++.h>
    #define N 5009
    using namespace std;
    typedef long long ll;
    int n,m,Q,head[N],tot,f[N];
    char s[N];
    bool ans[N][N],co[N],tag[N];
    inline ll rd(){
    	ll x=0;char c=getchar();bool f=0;
    	while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
    	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    	return f?-x:x;
    }
    struct node{int x,y;};
    vector<node>vec[2],nw;
    vector<node>::iterator it;
    queue<node>q;
    struct edge{int n,to;}e[N*N];
    inline void add(int u,int v){
    	e[++tot].n=head[u];e[tot].to=v;head[u]=tot;
    	e[++tot].n=head[v];e[tot].to=u;head[v]=tot;
    }
    int find(int x){
      if(x==f[x])return x;
      int xx=find(f[x]);
      co[x]^=co[f[x]];
      return f[x]=xx;
    }
    int main(){
    	n=rd();m=rd();Q=rd();
    	scanf("%s",s+1);
    	int x,y;
    	for(int i=1;i<=m;++i){
    		x=rd();y=rd();
    		if(s[x]==s[y]){
    			vec[s[x]-'0'].push_back(node{x,y});
    			ans[x][y]=ans[y][x]=1;q.push(node{x,y});
    		}
    		else nw.push_back(node{x,y});
    	}
    	for(int i=1;i<=n;++i)f[i]=i,co[i]=tag[i]=0;
    	for(it=vec[0].begin();it!=vec[0].end();++it){
    		node x=*it;
    		int xx=find(x.x),yy=find(x.y);
    		if(xx!=yy){
    			f[xx]=yy;tag[yy]|=tag[xx];
    			co[xx]=co[x.x]^co[x.y]^1;
    			add(x.x,x.y);
    		}
    		else if(co[x.x]==co[x.y]&&!tag[yy]){
    			tag[yy]=1;
    			add(x.x,x.y);
    		}
    	}
    	for(it=vec[1].begin();it!=vec[1].end();++it){
    		node x=*it;
    		int xx=find(x.x),yy=find(x.y);
    		if(xx!=yy){
    			f[xx]=yy;tag[yy]|=tag[xx];
    			co[xx]=co[x.x]^co[x.y]^1;
    			add(x.x,x.y);
    		}
    		else if(co[x.x]==co[x.y]&&!tag[yy]){
    			tag[yy]=1;
    			add(x.x,x.y);
    		}
    	}
    	for(int i=1;i<=n;++i)f[i]=i;
    	for(it=nw.begin();it!=nw.end();++it){
    		node x=*it;
    		int xx=find(x.x),yy=find(x.y);
    		if(xx!=yy){
    			f[xx]=yy;
    			add(x.x,x.y);
    		}
    	}
    	for(int i=1;i<=n;++i)ans[i][i]=1,q.push(node{i,i});
    	while(!q.empty()){
    		node x=q.front();q.pop();
    		for(int i=head[x.x];i;i=e[i].n)
    		  for(int j=head[x.y];j;j=e[j].n){
    		  	int v1=e[i].to,v2=e[j].to;
    		  	if(ans[v1][v2]||s[v1]!=s[v2])continue;
    		  	ans[v1][v2]=ans[v2][v1]=1;
    		  	q.push(node{v1,v2});
    		  }
    	}
    	while(Q--){
    		x=rd();y=rd();
    		puts(ans[x][y]?"YES":"NO");
    	}
    	return 0;
    }
    
  • 相关阅读:
    sql number类型和varchar2类型
    B
    E
    D
    B
    A
    第三课 选区
    第二课 新建文件与图层
    第一课 界面认识
    CSS命名规则
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/10819934.html
Copyright © 2011-2022 走看看