zoukankan      html  css  js  c++  java
  • BZOJ3197:[SDOI2013]刺客信条——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=3197

    故事发生在1486 年的意大利,Ezio 原本只是一个文艺复兴时期的贵族,后来因为家族成员受到圣殿骑士的杀害,决心成为一名刺客。最终,凭借着他的努力和出众的天赋,成为了杰出的刺客大师,他不仅是个身手敏捷的武林高手,飞檐走壁擅长各种暗杀术。刺客组织在他的带领下,为被剥削的平民声张正义,赶跑了原本统治意大利的圣殿骑士首领-教皇亚历山大六世。在他的一生中,经历了无数次惊心动魄、扣人心弦的探险和刺杀。

    曾经有一次,为了寻找Altair 留下的线索和装备,Ezio 在佛罗伦萨中的刺客墓穴进行探索。这个刺客墓穴中有许多密室,且任何两个密室之间只存在一条唯一的路径。这些密室里都有一个刺客标记,他可以启动或者关闭该刺客标记。为了打开储存着线索和装备的储藏室,Ezio 必须操作刺客标记来揭开古老的封印。要想解开这个封印,他需要通过改变某些刺客标记的启动情况,使得所有刺客标记与封印密码“看起来一样”。

    在这里,“看起来一样”的定义是:存在一种“标记”密室与“密码”密室之间一一对应的关系,使得密室间的连接情况和启动情况相同(提示中有更详细解释)。幸运的是,在Ezio 来到刺客墓穴之前,在Da Vinci 的帮助下,Ezio 已经得知了打开储藏室所需要的密码。

    而你的任务则是帮助Ezio 找出达成目标所需要最少的改动标记次数。

    参考:https://www.luogu.org/blog/user29936/solution-p3296

    多半是树哈希判同构了。

    设$f[u][v]$表示$u$子树和$v$子树同构且同层的情况下$u$子树原标记变动成$v$子树的新标记需要的最少次数。

    那么实际上就是枚举$u$和$v$的儿子子树互相匹配,用他们的$f$转移到$f[u][v]$上,很明显这是一个带权二分图匹配的过程,KM是一个很好的选择。

    如果要是枚举根来做的话,复杂度就是$O(1331n^2)$过不了,当然如果你使用动态换根的话,虽然我没有想过,但是到目前位置,代码已经快200行了,如果再动态换根的话就要累死了(当然debug就更累了)。

    我们有个很妙的性质:取这棵树的重心(如果有两个重心,则将两个重心之间的边上建这个点,取这个点)作为根。

    因为我们有一个美妙的结论:两棵树同构,当且仅当以两棵树重心为根的树同构。

    (其实我也不知道为什么233可能是此时同构的对是最多的吧……)

    #include<map>
    #include<cmath>
    #include<stack>
    #include<queue>
    #include<cstdio>
    #include<cctype>
    #include<vector>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    typedef unsigned long long ll;
    const int M=12;
    const int N=705;
    const int B=6662333;
    const int INF=1e9;
    inline int read(){
        int X=0,w=0;char ch=0;
        while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
        while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
        return w?-X:X;
    }
    int dis[M][M],wx[M],wy[M],match[M],sla[M];
    bool vx[M],vy[M];
    bool dfs2(int u,int n){
        vx[u]=1;
        for(int v=1;v<=n;v++){       
            if(!vy[v]){
                int w=wx[u]+wy[v]-dis[u][v];
                if(!w){
                    vy[v]=1;
                    if(!match[v]||dfs2(match[v],n)){
                        match[v]=u;return 1;
                    }
                }else sla[v]=min(sla[v],w);
            }
        }
        return 0;
    }
    int KM(int n){
        memset(wx,-127,sizeof(wx));
        memset(wy,-127,sizeof(wy));
        memset(match,0,sizeof(match));
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                wx[i]=max(wx[i],dis[i][j]);
        for(int i=1;i<=n;i++){
            memset(sla,127,sizeof(sla));
            while(1){
                memset(vx,0,sizeof(vx));
                memset(vy,0,sizeof(vy));
                if(dfs2(i,n))break;
                int minn=INF;
                for(int j=1;j<=n;j++)
                    if(!vy[j])minn=min(minn,sla[j]);
                for(int j=1;j<=n;j++){
                    if(vx[j])wx[j]-=minn;
                    if(vy[j])wy[j]+=minn;
                    else sla[j]-=minn;
                }
            }
        }
        int ans=0;
        for(int i=1;i<=n;i++)if(dis[match[i]][i]!=-INF)ans-=dis[match[i]][i];
        return ans;
    }
    struct node{
        int to,nxt;
    }e[N*2];
    int n,cnt,head[N],fa[N],a[N],b[N],q[N],size[N],son[N],dep[N];
    ll h[N];
    inline void add(int u,int v){
        e[++cnt].to=v;e[cnt].nxt=head[u];head[u]=cnt;
    }
    int calcg(int st){
        int r=0,g1,g2=0,maxn=n;
        q[++r]=st;fa[st]=0;
        for(int l=1;l<=r;l++){
        int u=q[l];size[u]=1;son[u]=0;
        for(int i=head[u];i!=-1;i=e[i].nxt){
            int v=e[i].to;
            if(v==fa[u])continue;
            fa[v]=u;q[++r]=v;
        }
        }
        for(int l=r;l>=1;l--){
        int u=q[l],v=fa[u];
        if(r-size[u]>son[u])son[u]=r-size[u];
        if(son[u]<maxn)g1=u,maxn=son[u],g2=0;
        else if(son[u]==maxn)g2=u;
        if(!v)break;
        size[v]+=size[u];
        if(size[u]>son[v])son[v]=size[u];
        }
        if(!g2)return g1;
        for(int i=head[g1];i!=-1;i=e[i].nxt){
        int v=e[i].to;
        if(v==g2){
            e[i].to=++n;e[i^1].to=n;
            add(n,g1);add(n,g2);
            return n;
        }
        }
    }
    ll dfs1(int u){
        ll num[N];int r=0;
        size[u]=1;h[u]=B;
        for(int i=head[u];i!=-1;i=e[i].nxt){
        int v=e[i].to;
        if(v==fa[u])continue;
        fa[v]=u;dep[v]=dep[u]+1;num[++r]=dfs1(v);size[u]+=size[v];
        }
        sort(num+1,num+r+1);
        for(int i=1;i<=r;i++)h[u]=h[u]*B+num[i];
        return h[u]*=size[u];
    }
    int f[N][N],to1[N],to2[N];
    int find(int u1,int u2){
        int idx1=0,idx2=0;
        memset(to1,0,sizeof(to1));
        memset(to2,0,sizeof(to2));
        for(int i=head[u1];i!=-1;i=e[i].nxt){
        int v1=e[i].to;if(v1==fa[u1])continue;
        if(!to1[v1])to1[v1]=++idx1;
        for(int j=head[u2];j!=-1;j=e[j].nxt){
            int v2=e[j].to;if(v2==fa[u2])continue;
            if(!to2[v2])to2[v2]=++idx2;
            //printf("1 %d %d %d
    ",v1,v2,f[v1][v2]);
            dis[to1[v1]][to2[v2]]=-f[v1][v2];
        }
        }
        return KM(idx1)+(a[u1]!=b[u2]);
    }
    int bfs(int s){
        int r=0;
        q[++r]=s;
        for(int l=0;l<=r;l++){
        int u=q[l];
        for(int i=head[u];i!=-1;i=e[i].nxt){
            int v=e[i].to;
            if(v==fa[u])continue;
            q[++r]=v;
        }
        }
        for(int i=r;i>=0;i--){
        if(dep[q[i]]!=dep[q[r]]){
            i++;
            for(int j=r;j>=i;j--)
            for(int k=r;k>=i;k--)
                if(h[q[j]]==h[q[k]]){
                f[q[j]][q[k]]=find(q[j],q[k]);
                //printf("%d %d %d
    ",q[j],q[k],f[q[j]][q[k]]);
                }
            r=i-1;
        }
        }
        return find(s,s);
    }
    int main(){
        cnt=-1,memset(head,-1,sizeof(head));
        n=read();
        for(int i=1;i<n;i++){
        int u=read(),v=read();
        add(u,v);add(v,u);
        }
        for(int i=1;i<=n;i++)a[i]=read();
        for(int i=1;i<=n;i++)b[i]=read();
        int rt=calcg(1);fa[rt]=0;
        dfs1(rt);
        for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)f[i][j]=INF;
        printf("%d
    ",bfs(rt));
        return 0;
    }

    +++++++++++++++++++++++++++++++++++++++++++

    +本文作者:luyouqi233。               +

    +欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

    +++++++++++++++++++++++++++++++++++++++++++

  • 相关阅读:
    Linux内核RPC请求过程
    二分图
    Java实现 蓝桥杯 算法提高 合并石子
    Java实现 蓝桥杯 算法提高 合并石子
    Java实现 蓝桥杯 算法提高 摩尔斯电码
    Java实现 蓝桥杯 算法提高 摩尔斯电码
    Java实现 蓝桥杯 算法提高 文本加密
    Java实现 蓝桥杯 算法提高 文本加密
    Java蓝桥杯 算法提高 九宫格
    Java蓝桥杯 算法提高 九宫格
  • 原文地址:https://www.cnblogs.com/luyouqi233/p/9192742.html
Copyright © 2011-2022 走看看