• Bzoj3197: [Sdoi2013]assassin


    题面

    传送门

    Sol

    套路:找出重心,如果有两个就新建一个点
    然后把这棵树hash一下
    (f[i][j])表示第一颗树到(i)第二棵树到(j),子树(i,j)同构的付出的最小代价
    转移:每次把这一层hash值相同的点做一边二分图权匹配(KM/费用流)就好了
    一遍AC

    # include <bits/stdc++.h>
    # define IL inline
    # define RG register
    # define Fill(a, b) memset(a, b, sizeof(a))
    using namespace std;
    typedef long long ll;
    
    IL int Input(){
    	RG int x = 0, z = 1; RG char c = getchar();
    	for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    	for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    	return x * z;
    }
    
    const int maxn(1005);
    const int inf(1e9);
    const ll seed1(9377);
    const ll seed2(10007);
    
    namespace MCMF{
    	int dis[maxn], pre1[maxn], pre2[maxn], vis[maxn], first[maxn], cnt, ans, s, t;
    	queue <int> q;
    
    	struct Edge{
    		int to, next, f, w;
    	} edge[maxn * maxn * 2];
    
    	IL void Init(RG int a, RG int b){
    		s = a, t = b, ans = cnt = 0;
    		for(RG int i = a; i <= b; ++i) first[i] = -1;
    	}
    	
    	IL void Add(RG int u, RG int v, RG int f, RG int w){
    		edge[cnt] = (Edge){v, first[u], f, w}, first[u] = cnt++;
    		edge[cnt] = (Edge){u, first[v], 0, -w}, first[v] = cnt++;
    	}
    
    	IL int Aug(){
    		for(RG int i = s; i <= t; ++i) dis[i] = inf;
    		q.push(s), dis[s] = 0, vis[s] = 1;
    		while(!q.empty()){
    			RG int u = q.front(); q.pop();
    			for(RG int e = first[u]; e != -1; e = edge[e].next){
    				RG int v = edge[e].to;
    				if(edge[e].f && dis[v] > dis[u] + edge[e].w){
    					dis[v] = dis[u] + edge[e].w;
    					pre1[v] = e, pre2[v] = u;
    					if(!vis[v]) q.push(v), vis[v] = 1;
    				}
    			}
    			vis[u] = 0;
    		}
    		if(dis[t] == inf) return 0;
    		RG int ret = inf;
    		for(RG int p = t; p; p = pre2[p]) ret = min(ret, edge[pre1[p]].f);
    		ans += ret * dis[t];
    		for(RG int p = t; p; p = pre2[p])
    			edge[pre1[p]].f -= ret, edge[pre1[p] ^ 1].f += ret;
    		return 1;
    	}
    
    	IL int Calc(){
    		for(ans = 0; Aug(); );
    		return ans;
    	}
    }
    
    int cnt, first[maxn];
    
    struct Edge{
    	int to, next;
    } edge[maxn << 1];
    
    namespace Hash{
    	struct Val{
    		unsigned long long f1, f2;
    
    		IL int operator ==(RG Val b) const{
    			return f1 == b.f1 && f2 == b.f2;
    		}
    	} v[maxn];
    
    	IL int Cmp(RG int a, RG int b){
    		return v[a].f1 != v[b].f1 ? v[a].f1 < v[b].f1 : v[a].f2 < v[b].f2;
    	}
    
    	int size[maxn];
    
    	IL void GetHash(RG int u, RG int ff){
    		vector <int> son; son.clear(), size[u] = 1;
    		for(RG int e = first[u]; e != -1; e = edge[e].next){
    			RG int v = edge[e].to;
    			if(v != ff){
    				GetHash(v, u);
    				size[u] += size[v], son.push_back(v);
    			}
    		}
    		sort(son.begin(), son.end(), Cmp);
    		for(RG int i = 0, l = son.size(); i < l; ++i){
    			v[u].f1 = v[u].f1 * seed1 + v[son[i]].f1 * seed1 + v[son[i]].f2 * seed2;
    			v[u].f2 = v[u].f2 * seed2 + v[son[i]].f1 * seed2 + v[son[i]].f2 * seed1;
    		}
    		v[u].f1 = v[u].f1 * seed1 + size[u];
    		v[u].f2 = v[u].f2 * seed2 + size[u];
    	}
    };
    
    int n, size[maxn], c1[maxn], c2[maxn], rt, tmp;
    int f[maxn][maxn];
    
    IL void Add(RG int u, RG int v){
    	edge[cnt] = (Edge){v, first[u]}, first[u] = cnt++;
    }
    
    IL void GetRoot(RG int u){
    	size[u] = 1; RG int mx = 0;
    	for(RG int e = first[u]; e != -1; e = edge[e].next){
    		RG int v = edge[e].to;
    		if(!size[v]){
    			GetRoot(v);
    			size[u] += size[v];
    			mx = max(mx, size[v]);
    		}
    	}
    	mx = max(mx, n - size[u]);
    	if((mx << 1) <= n){
    		if(!rt) rt = u;
    		else tmp = u;
    	}
    }
    
    IL void Solve(RG int u1, RG int u2, RG int ff1, RG int ff2){
    	f[u1][u2] = c1[u1] != c2[u2];
    	vector <int> son1, son2; son1.clear(), son2.clear();
    	for(RG int e = first[u1]; e != -1; e = edge[e].next)
    		if(edge[e].to != ff1) son1.push_back(edge[e].to);
    	for(RG int e = first[u2]; e != -1; e = edge[e].next)
    		if(edge[e].to != ff2) son2.push_back(edge[e].to);
    	RG int l1 = son1.size(), l2 = son2.size(), s = 0, t = l1 + l2 + 1;
    	for(RG int i = 0; i < l1; ++i)
    		for(RG int j = 0; j < l2; ++j)
    			if(Hash::v[son1[i]] == Hash::v[son2[j]]) Solve(son1[i], son2[j], u1, u2);
    	MCMF::Init(0, t);
    	for(RG int i = 0; i < l1; ++i)
    		for(RG int j = 0; j < l2; ++j)
    			if(Hash::v[son1[i]] == Hash::v[son2[j]]) MCMF::Add(i + 1, j + 1 + l1, 1, f[son1[i]][son2[j]]);
    	for(RG int i = 0; i < l1; ++i) MCMF::Add(s, i + 1, 1, 0);
    	for(RG int i = 0; i < l2; ++i) MCMF::Add(i + 1 + l1, t, 1, 0);
    	RG int ret = MCMF::Calc();
    	f[u1][u2] += ret;
    }
    
    int main(){
    	n = Input();
    	for(RG int i = 1; i <= n + 1; ++i) first[i] = -1;
    	for(RG int i = 1; i < n; ++i){
    		RG int u = Input(), v = Input();
    		Add(u, v), Add(v, u);
    	}
    	for(RG int i = 1; i <= n; ++i) c1[i] = Input();
    	for(RG int i = 1; i <= n; ++i) c2[i] = Input();
    	GetRoot(1);
    	if(tmp){
    		for(RG int e = first[rt]; e != -1; e = edge[e].next)
    			if(edge[e].to == tmp) edge[e].to = n + 1;
    		for(RG int e = first[tmp]; e != -1; e = edge[e].next)
    			if(edge[e].to == rt) edge[e].to = n + 1;
    		Add(n + 1, rt), Add(n + 1, tmp), rt = n + 1;
    	}
    	Hash::GetHash(rt, 0);
    	Solve(rt, rt, 0, 0);
    	printf("%d
    ", f[rt][rt]);
    	return 0;
    }
    
    
  • 相关阅读:
    问题——虚拟机连接,查本地DNS,查软件位置,payload生成,检测注册表变化
    nmap命令解释
    SMB扫描,SMTP扫描
    操作系统识别,SNMP扫描
    服务扫描——查询banner信息,服务识别
    nmap之扫描端口(附加hping3隐藏扫描)
    scapy简单用法——四层发现
    转载 界面组装器模式
    设计模式=外观模式
    如何进行自动化测试和手工测试
  • 原文地址:https://www.cnblogs.com/cjoieryl/p/9113950.html
Copyright © 2020-2023  润新知