Luogu P4551 最长异或路径 (字符串,01Trie)

题目链接:https://www.luogu.com.cn/problem/P4551

题意:给定n个点(1<=n<=1e5),n-1条带权无向边 u,v,w。求最大的异或路径,即所有最短路径中的异或和的最大值。

题解:01Trie模板题

1.求num[i]:首先在原树上随便选择一个点作为根节点s,然后num[i]表示表示根节点到点i的路径异或和,那么任意两点j,k之间的路径异或和为num[j]^num[k]。

2.利用num[i]建立二进制Trie树:优化空间,空间复杂度降到大概O(n*30)。

3.求最大路径异或和:ans=max(ans,query(num[i]))。query返回的是num[i]与某一个num[j]的最大值,一次查询时间复杂度大概只需要O(30)。具体操作见代码

总结:这题是很模板的01Trie,提供了新的解题思路。

算法学习重深度还是广度。我的选择是:能深度尽量深度,疲惫之后就广度,把字符串的只是全部联结起来,然后不断突破自己即可。

好好总结一下模板!

代码:

#include <bits/stdc++.h>

#define ll long long
#define pi acos(-1)
#define pb push_back
#define mst(a, i) memset(a, i, sizeof(a))
#define pll pair<ll, ll>
#define fi first
#define se second
#define mp(x,y) make_pair(x,y)
#define rep(i,a,n)  for(ll i=a;i<=n;i++)
#define per(i,n,a)  for(ll i=n;i>=a;i--)
#define dbg(x) cout << #x << "===" << x << endl
#define dbgg(l,r,x) for(ll i=l;i<=r;i++) cout<<x[i]<<" ";cout<<"<<<"<<#x;cout<<endl

using namespace std;

template<class T>void read(T &x){T res=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){res=(res<<3)+(res<<1)+c-'0';c=getchar();}x=res*f;}
void print(ll x){if(x<0){putchar('-');x=-x;}if(x>9)print(x/10);putchar(x%10+'0');}
const ll maxn = 1e5 + 10;
const ll mod = 1e9+7;

ll n,u,v,w;
ll num[maxn];

struct Trie_01{
	ll trie[maxn*30][2],val[maxn*30],cnt;//注意cnt的最大值 
	vector<pll > g[maxn];
	//01Trie插入数 
	void insert(ll s){
		ll u=0;
		for(ll i=30;i>=0;i--){
			ll t=1<<i;//dbg(t),标记一下,检查是否溢出;
			bool x=s&t;//注意应是bool型,不然x=0/t,而非0/1 
			if(!trie[u][x]) trie[u][x]=++cnt;
			u=trie[u][x]; 
		}
		val[u]=1;
	}
	//返回与s异或得到的最大值 
	ll query(ll s){
		ll res=0,u=0;
		for(ll i=30;i>=0;i--){
			ll t=1<<i;//dbg(t);
			bool x=s&t;
//			cout<<">>>"<<i<<" "<<x<<endl;
			if(trie[u][!x]) res+=t,u=trie[u][!x];
			else u=trie[u][x];
		}
		return res;
	}
	//最开始给的是一颗带权无向边树,dfs求num[i] 
	void dfs(ll x,ll fa){
		for(auto i:g[x]){
			if(i.fi==fa) continue;
			num[i.fi]=i.se^num[x];//?num[i]表示根节点到i点的路径异或和 
			dfs(i.fi,x);
		}
	}
}T;
int main() {
    ll _s = 1;
    // read(_s);
    //freopen("testdata.in","r",stdin);
	//freopen("testout.out","w",stdout);
    for (ll _=1;_<=_s;_++) {
        read(n);
        rep(i,1,n-1){
        	read(u),read(v),read(w);
        	T.g[u].pb(mp(v,w));
        	T.g[v].pb(mp(u,w));
		}
		T.dfs(1,-1);
//		dbgg(1,n,num);
		rep(i,1,n) T.insert(num[i]); 
		ll ans=0;
		rep(i,1,n) ans=max(ans,T.query(num[i])); 
		cout<<ans<<endl;
    }
    return 0;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值