2023NOIP A层联测30-草莓路径

Madeline 有一张 n n n 个点 m m m 条边的无向联通图(可能存在重边、自环)。对于一条连接 u i u_i ui v i v_i vi 的边,这条边上有 w i w_i wi 个草莓。定义一条路径的草莓值为这条路径的所有边上的草莓数量的异或和。

Madeline最终能吃到草莓值这么多的草莓,所以请你找出草莓值最大的路径,输出这个值。注意路径的起点和终点可以相同。

n , m ≤ 1 0 5 , w i ≤ 1 0 18 n,m\le10^5,w_i\le10^{18} n,m105,wi1018


这里的异或和是指依次经过的边权的异或,意思是如果一条边重复经过两次,那么就要异或两次。赛时看错题了,暴力没分。

考虑两个点之间的路径的异或和有什么性质。任取一个生成树,容易发现两点之间的路径的异或和都可以表示成树上两点路径异或和与图上若干个环的异或和。

感性证明:其他的路径一定是在树上路径上把中间的几段链换成别的链或环,模拟一下就会发现是满足条件的。

所以 dfs 一遍找出所有的环,求出异或和丢进线性基,然后再枚举两个点,然后求树上路径异或和 w w w 与线性基内元素异或的最大值:从高位开始枚举,如果 w w w i i i 位为 0 0 0,就异或上 d u d_u du。时间复杂度 O ( n 2 log ⁡ V ) O(n^2\log V) O(n2logV)

考虑优化,此时问题转化为从集合 A A A 中取两个数 a i , a j a_i,a_j ai,aj,在集合 B B B 中取若干数(设异或和为 b b b),让取出的数的异或和最大,就是 max ⁡ ( a i ⊕ a j ⊕ b ) \max(a_i\oplus a_j\oplus b) max(aiajb)

b = b 1 ⊕ b 2 b=b_1\oplus b_2 b=b1b2,答案即为 max ⁡ ( max ⁡ ( a i ⊕ b 1 ) ⊕ min ⁡ ( a j ⊕ b 2 ) ) \max(\max(a_i\oplus b_1) \oplus\min(a_j\oplus b_2)) max(max(aib1)min(ajb2)),感性理解,我们要尽可能让前者高位含有 1 1 1 尽可能多,让后者高位含有的 1 1 1 尽可能少。

max ⁡ ( a i ⊕ b 1 ) \max(a_i\oplus b_1) max(aib1),直接线性基,若当前异或上 d i d_i di 更大,就更新;求 min ⁡ ( a j ⊕ b 2 ) \min(a_j\oplus b_2) min(ajb2),就先把 a j a_j aj 翻转,求完最大值然后再翻转回来。

求二者异或最大值可以在 01tire 上做,每次就尽可能往相反的方向走。

时间复杂度 O ( n log ⁡ V ) O(n\log V) O(nlogV),其中 V V V 是值域。

#include
using namespace std;
#define ll unsigned long long
const int N=1e5+1;
int n,m,cnt=1,tr[N*61][2],vis[N];
ll ans,d[61],val[N];
vector<pair<int,ll> > v[N];
void insert(ll x)
{
    for(int i=60;i>=0;i--){
        if((x>>i)&1){
            if(!d[i]){d[i]=x;break;}
            else x^=d[i];
        }
    }
}
void Insert(ll n)
{
    int rt=1;
    for(int i=60;i>=0;i--){
        int x=n>>i&1;
        if(!tr[rt][x]) tr[rt][x]=++cnt;
        rt=tr[rt][x];
    }
}
ll query(ll n)
{
    int rt=1;
    ll ans=0;
    for(int i=60;i>=0;i--){
        int x=n>>i&1;
        if(tr[rt][x^1]) ans|=1ll<<i,rt=tr[rt][x^1];
        else rt=tr[rt][x];
    }
    return ans;
}
void dfs1(int u,int fa)
{
    vis[u]=1;
    for(auto i:v[u]){
        if(vis[i.first]){
            insert(val[u]^i.second^val[i.first]);
            continue;
        }
        val[i.first]=val[u]^i.second;
        dfs1(i.first,u);
    }
}
ll getmax(ll n)
{
    for(int i=60;i>=0;i--) if((n^d[i])>n) n^=d[i];
    return n;
}
signed main()
{
    freopen("path.in","r",stdin);
    freopen("path.out","w",stdout);
    cin.tie(0)->sync_with_stdio(0);
    cin>>n>>m;
    for(int i=1,x,y;i<=m;i++){
        ll w;
        cin>>x>>y>>w;
        v[x].push_back(make_pair(y,w));
        v[y].push_back(make_pair(x,w));
    }
    dfs1(1,0);
    for(int i=1;i<=n;i++){
        Insert(getmax(val[i]));
        ans=max(ans,query(~getmax(~val[i])));
    }
    cout<<ans;
}

你可能感兴趣的:(算法)