1. 程式人生 > >BZOJ3197:[SDOI2013]刺客信條——題解

BZOJ3197:[SDOI2013]刺客信條——題解

TP node std 沒有 刺客信條 標記 algorithm getchar() sdi

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\n",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\n",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\n",bfs(rt));
    return 0;
}

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

+本文作者:luyouqi233。               +

+歡迎訪問我的博客:http://www.cnblogs.com/luyouqi233/+

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

BZOJ3197:[SDOI2013]刺客信條——題解