zoukankan      html  css  js  c++  java
  • [哈希][费用流]JZOJ 3296 【SDOI2013】刺客信条

    Description

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

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

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

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

    Input



    第一行给出一个整数n,表示密室的个数。

    第二行至第n 行,每行给出两个整数a 和b,表示第a 个密室和第b 个密室之间存在一条通道。

    第n+1 行,给出n 个整数,分别表示当时每个密室的启动情况(0 表示关闭,1 表示启动)。

    第n+2 行,给出n 个整数,分别表示密码中每个密室的启动情况。

    Output

    输出只有一行,即输出最少改动标记次数。
     

    Sample Input

    4
    1 2
    2 3
    3 4
    0 0 1 1
    1 0 0 0

    Sample Output

    1
     

    Data Constraint

    对于30%的数据,n<=10。

    对于60%的数据,n<=100。

    对于100%的数据,n<=700,且每个密室至多与11 个密室相通。
     

    Hint

    样例解释:

    密室的编号是可以变的!将第三个密室关闭后,在当前标记和密码之间,存在1->4,2->3,3->2,4->1 的对应关系,重新编号后连接情况没有改变,且标记与密码对应。对于更一般的情况,存在一个1 到n 的置换P,使得对于任意密室之间的道路u-v,都一定有密码密室中的道路P(u)-P(v);如果不存在密室之间的道路u-v,则一定没有密码密室中的道路P(u)-P(v)。

    分析

    看懂题意后思路会较为清晰虽然我也讲不清楚,请dalao们感性理解

    针对题目的动态根,我们可以考虑两棵树同用一个根,然后同构树有个神奇的性质:如果使强制使两棵树的重心为根,那么这两棵树同构当且仅当这两棵有根树同构

    但是重心可能有两个呢,我们只需要断开两个重心之间的边,然后新建一个点连向两个重心,新点就是新重心了

    我们可以想到树中同深度同构的部分可以交换以减少代价

    同构?树哈希咯

    处理出每个点的哈希值后,我们设f[u][v]为以u为根的子树和以v为根的子树同构,且完成u子树的初始状态到v子树的最终状态的最小代价

    显然这个要从他们的儿子处转移上来

    然后我们可以发现,转移的过程就是选择这两棵子树中的若干对点,选择一对的代价为f[su][sv],使总代价最小

    这是个带权二分图,可用KM算法或费用流算法解决

    建议使用KM,费用流算法需要注意空间大小,代码复杂

    #include <iostream>
    #include <cstdio>
    #include <memory.h>
    #include <algorithm>
    #include <queue>
    using namespace std;
    const int N=7e2+10;
    const int P=76447;
    struct Graph {
        int u,v,nx;
    }g[2*N];
    struct Cube {
        int u,v,nx,c,w;
    }t[2*N*N+4*N];
    int cnt,list[N],vis[2*N],dep[N],flow[2*N],dis[2*N],fa[2*N];
    int g1,g2,sz[N],mxsize;
    int h[N],xx[N],f[N][N],p[N];
    int n,from[N],to[N],ans;
    
    void Add(int u,int v) {
        g[++cnt]=(Graph){u,v,list[u]};list[u]=cnt;
        g[++cnt]=(Graph){v,u,list[v]};list[v]=cnt;
    }
    
    void Add_Cube(int u,int v,int w,int c) {
        t[++cnt]=(Cube){u,v,flow[u],c,w};flow[u]=cnt;
        t[++cnt]=(Cube){v,u,flow[v],0,-w};flow[v]=cnt;
    }
    
    void Find_G(int u,int f) {
        int size=0;
        sz[u]=1;
        for (int i=list[u];i;i=g[i].nx)
            if (g[i].v!=f) {
                Find_G(g[i].v,u);
                sz[u]+=sz[g[i].v];
                size=max(size,sz[g[i].v]);
            }
        size=max(size,n-sz[u]);
        if (size==mxsize) g2=u;
        if (size<mxsize) {
            mxsize=size;
            g1=u;
            g2=0;
        }
    }
    
    void Tree_Hash(int u,int f) {
        int xcnt=0;dep[u]=dep[f]+1;
        for (int i=list[u];i;i=g[i].nx) if (g[i].v!=f) Tree_Hash(g[i].v,u);
        for (int i=list[u];i;i=g[i].nx) if (g[i].v!=f) xx[++xcnt]=h[g[i].v];
        sort(xx+1,xx+xcnt+1);
        h[u]=17731;
        for (int i=1;i<=xcnt;i++) h[u]=(h[u]*7441%P+xx[i])%P;
        h[u]=h[u]*3119%P;
    }
    
    bool CMP(int u,int v) {
        return dep[u]!=dep[v]?dep[u]>dep[v]:h[u]<h[v];
    }
    
    bool SPFA() {
        queue<int> q;
        while (!q.empty()) q.pop();
        memset(dis,0x3f,sizeof dis);
        dis[0]=0;q.push(0);
        while (!q.empty()) {
            int u=q.front();q.pop();
            for (int i=flow[u];i;i=t[i].nx)
                if (dis[t[i].v]>dis[u]+t[i].w&&t[i].c) {
                    dis[t[i].v]=dis[u]+t[i].w;
                    fa[t[i].v]=i;
                    if (!vis[t[i].v]) q.push(t[i].v);
                    vis[t[i].v]=1;
                }
            vis[u]=0;
        }
        return dis[2*n+1]!=0x3f3f3f3f;
    }
    
    void Get_Ans() {
        int x=2*n+1,mf=2147483647;
        while (fa[x]) {
            ans+=t[fa[x]].w;
            mf=min(mf,t[fa[x]].c);
            x=t[fa[x]].u;
        }
        x=2*n+1;
        while (fa[x]) {
            t[fa[x]].c-=mf;
            t[fa[x]^1].c+=mf;
            x=t[fa[x]].u;
        }
    }
    
    int Min_Cost_Flow() {
        ans=0;
        while (SPFA()) Get_Ans();
        return ans;
    }
    
    void DP() {
        for (int i=1;i<=n;i++) p[i]=i;
        sort(p+1,p+n+1,CMP);
        for (int i=1;i<=n;) {
            int j=i;
            for (;j<=n;j++)
                if (dep[p[i]]!=dep[p[j]]||h[p[i]]!=h[p[j]]) break;
            for (int k=i;k<j;k++)
                for (int l=i;l<j;l++) {
                    int u=p[k],v=p[l];
                    cnt=1;memset(flow,0,sizeof flow);
                    for (int sv=list[v];sv;sv=g[sv].nx)
                        if (dep[g[sv].v]==dep[v]+1)
                            Add_Cube(n+g[sv].v,2*n+1,0,1);
                    for (int su=list[u];su;su=g[su].nx)
                        if (dep[g[su].v]==dep[u]+1) {
                            Add_Cube(0,g[su].v,0,1);
                            for (int sv=list[v];sv;sv=g[sv].nx)
                                if (dep[g[sv].v]==dep[v]+1&&h[g[su].v]==h[g[sv].v])
                                    Add_Cube(g[su].v,n+g[sv].v,f[g[su].v][g[sv].v],1);
                        }
                    f[u][v]=Min_Cost_Flow()+(from[u]!=to[v]);
                }
            i=j;
        }
    }
    
    int main() {
        scanf("%d",&n);
        for (int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),Add(u,v);
        for (int i=1;i<=n;i++) scanf("%d",&from[i]);
        for (int i=1;i<=n;i++) scanf("%d",&to[i]);
        mxsize=n;
        Find_G(1,0);
        if (g2) {
            cnt=0;memset(list,0,sizeof list);
            for (int i=1,j=1;i<=n-1;i++,j=i*2-1)
                if ((g[j].u!=g1||g[j].v!=g2)&&(g[j].v!=g1||g[j].u!=g2))
                    Add(g[j].u,g[j].v);
            Add(g1,n+1),Add(n+1,g2);
            n++;
            g1=n;
        }
        Tree_Hash(g1,0);
        DP();
        printf("%d",f[g1][g1]);
    }
    View Code
    在日渐沉没的世界里,我发现了你。
  • 相关阅读:
    Java多线程编程核心技术---对象及变量的并发访问(二)
    Java多线程编程核心技术---对象及变量的并发访问(一)
    Java多线程编程核心技术---Java多线程技能
    普通浏览器GET请求与Ajax的GET请求的区别
    【python】-- 面向对象引子、概念
    【python】-- json & pickle、xml、requests、hashlib、shelve、shutil、configparser、subprocess
    【python】-- 内置函数、软件目录开发规范(代码编码风格)
    【python】-- 装饰器、迭代器、生成器
    【python】-- 递归函数、高阶函数、嵌套函数、匿名函数
    【python】-- 函数非固定参数,返回值(return)
  • 原文地址:https://www.cnblogs.com/mastervan/p/11104754.html
Copyright © 2011-2022 走看看