zoukankan      html  css  js  c++  java
  • [HNOI2019]校园旅行(建图优化+bfs)

    30分的O(m^2)做法应该比较容易想到:令f[i][j]表示i->j是否有解,然后把每个路径点数不超过2的有解状态(u,v)加入队列,然后弹出队列时,两点分别向两边搜索边,发现颜色一样时,再修改答案,加入队列即可。

    100分是挺难想的,是个思维题,可以把边分成连接同色和异色两种。发现走过的路径一定是若干同色连通块拼接而成,除了中间的连通块外,其余长度均相等。对于长度,如果短,可以反复走把长度走到相等,重点是奇偶性要相同。所以,我们能够联想和二分图有关的东西。异色连通块,很显然是二分图,于是我们可以直接建立生成树。而同色的,如果是二分图则无需改变奇偶性,也连成一棵树。反之,则在树上连个自环即可,这样可以改变奇偶性。复杂度O(n^2)。据说HNOI今年卡栈,写dfs的都爆零?算了AH已经不和HN联考也不管了,反正OJ上过了……

    #include<bits/stdc++.h>
    using namespace std;
    typedef pair<int,int>pii;
    const int N=5005,M=1e6+7;
    int n,m,Q,cnt,fa[N],hd[N],v[M],nxt[M],col[N];
    char str[N];
    bool f[N][N];
    vector<int>G[N];
    queue<pii>q;
    int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
    void add(int x,int y){v[++cnt]=y,nxt[cnt]=hd[x],hd[x]=cnt;}
    bool dfs(int u,int c)
    {
        col[u]=c;
        bool ret=0;
        for(int i=0;i<G[u].size();i++)
        if(col[G[u][i]]==-1)
        {
            add(u,G[u][i]),add(G[u][i],u);
            ret|=dfs(G[u][i],c^1);
            f[u][G[u][i]]=f[G[u][i]][u]=1;
            q.push(pii(min(u,G[u][i]),max(u,G[u][i])));
        }
        else if(col[G[u][i]]==c)ret=1;
        return ret;
    }
    int main()
    {
        scanf("%d%d%d",&n,&m,&Q);
        scanf("%s",str+1);
        for(int i=1;i<=n;i++)fa[i]=i;
        for(int i=1,x,y;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            if(str[x]==str[y])G[x].push_back(y),G[y].push_back(x);
            else{
                int u=find(x),v=find(y);
                if(u!=v)fa[u]=v,add(x,y),add(y,x);
            }
        }
        memset(col,-1,sizeof col);
        for(int i=1;i<=n;i++)if(col[i]==-1&&dfs(i,0))add(i,i);
        for(int i=1;i<=n;i++)f[i][i]=1,q.push(pii(i,i));
        while(!q.empty())
        {
            int x=q.front().first,y=q.front().second;
            q.pop();
            for(int i=hd[x];i;i=nxt[i])
            for(int j=hd[y];j;j=nxt[j])
            if(str[v[i]]==str[v[j]]&&!f[v[i]][v[j]])
            f[v[i]][v[j]]=f[v[j]][v[i]]=1,q.push(pii(min(v[i],v[j]),max(v[i],v[j])));
        }
        while(Q--)
        {
            int x,y;scanf("%d%d",&x,&y);
            if(f[x][y])puts("YES");else puts("NO");
        }
    }
    View Code
  • 相关阅读:
    前端与算法 leetcode 344. 反转字符串
    JavaScript闭包使用姿势指南
    前端与算法 leetcode 48. 旋转图像
    前端与算法 leetcode 36. 有效的数独
    前端与算法 leetcode 1. 两数之和
    前端与算法 leetcode 283. 移动零
    前端与编译原理 用js去运行js代码 js2run
    前端与算法 leetcode 66. 加一
    前端与算法 leetcode 350. 两个数组的交集 II
    前端与算法 leetcode 26. 删除排序数组中的重复项
  • 原文地址:https://www.cnblogs.com/hfctf0210/p/10800719.html
Copyright © 2011-2022 走看看