CF786Div3-G

原题链接

分析

将题目分析,我们可以精简出题目:给定一个无向有权图,要求我们求出边权或之后的生成树。

很自然的可以想到,若想要或值最小,那自然是想要生成树中的n-1条边权的高位尽量全为0。

但显而易见会遇到的一个问题是:若高位想确认是0还是1,则势必会影响接下来位的边的选择。

因此,如何保存下来高位的影响是一个问题。

这里,我们配合着代码说。

	int ans = 0;
    for(int i=31;i>=0;i--){//枚举每一个数位
        int res = ans;
        for(int j=i-1;j>=0;j--) res|=(1<<j);//将比该位低的位全部置1,为后面选边进行准备,
        vector<PIII> v;
        for(int j=0;j<m;j++)//这是精华之处。进行选边,我们只保留满足高位要求,且当前位为0的边。
            if((edges[j].w|res)<=res)
                v.push_back({edges[j].w,{edges[j].u,edges[j].v}});
        int cnt = 0;
        for(int j=1;j<=n;j++) p[j]=j;
        for(auto x:v){
            int a = find(x.second.first),b = find(x.second.second),w = x.first;
            if(a!=b){
                p[a]=b;
                cnt++;//用来记录,对于该位来说,有多少条边在该位上为0;
                if(cnt==n-1) break;//若是已经够了则退出
            }
        }
        if(cnt<n-1) ans|=1<<i;//若是该位上为0的边数不够,则该位上为1.
    }

我们主要对核心操作进行解释

int res = ans;
for(int j=i-1;j>=0;j--) res|=(1<<j);//将比该位低的位全部置1,为后面选边进行准备,
vector<PIII> v;
 for(int j=0;j<m;j++)//这是精华之处。进行选边,我们只保留满足高位要求,且当前位为0的边。
            if((edges[j].w|res)<=res)
                v.push_back({edges[j].w,{edges[j].u,edges[j].v}});

我们首先考虑高位分别置0,1后面选边的影响

  • 若高位为0,则后面的选边只能从,高位为0边中选
  • 若高位为1,则后面选边无限制

那如何来限制呢?

我们要利用或运算的性质,将比i位低的位全部置1,这样低位就是无影响的了。

然后将高位继承下来,因为高位的结果全部在ans中,我们直接继承就可以。

(edges[j].w|res)<=res

利用上边的限制,若是确定的答案中高位为0,则若是边中对应高位为1,就会有(edges[j].w|res)>res。

同时因为res在第i位上是0,则若是边中对应第i位为1,就会有(edges[j].w|res)>res。

综上就可以将边进行筛除,剩下可以进行选择的边,全部都为符合条件的边。

AC_CODE

#include <bits/stdc++.h>
#define fi first    
#define se second    
#define debug(x) cout<<#x" ----> "<<x<<endl
#define endl '\n'
#define fix(a) fixed << setprecision(a)
#define rep(i, b, s) for(int i = (b); i <= (s); ++i)
#define pre(i, b, s) for(int i = (b); i >= (s); --i)
#define TASK int _; read(_); while(--_)
#define ios ios::sync_with_stdio(false); cin.tie(0), cout.tie(0)
#define all(v) (v).begin(),(v).end()
#define mk make_pair
using namespace std;
 
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII ;
typedef pair<int, PII> PIII ;
typedef pair<double,double> PDD;
const double eps = 1e-10;
const double pi = acos(-1.0);
const LL LINF = INT64_MAX;
const int INF = INT_MAX,mod = 1e9 + 7;
template <typename T> T lowbit(T &x) { return x & (-x);}
template <typename T> T gcd(T a, T b) {return b ? gcd(b, a%b) : a;}
LL ksm(LL a,LL b,LL p = mod) {LL res = 1;a%=p;while(b){ if(b&1) res = res*a%p; a=a*a%p;b>>=1;} return res%p;}
template <typename T> T lcm(T a, T b) {return a / gcd(a, b) * b;}
inline bool valid(char c) { return 33<=c&&c<=126; }
template < typename T >
inline void read(T &x)
{
x = 0; bool f = 0; char ch = getchar();
while(!isdigit(ch)){f ^= !(ch ^ 45);ch=getchar();}
while(isdigit(ch)) x= (x<<1)+(x<<3)+(ch&15),ch=getchar();
x = f ? -x : x;
}
template<typename T,typename ...Args>void read(T &x, Args &...args)
{ read(x),read(args...); }
template <typename T> void read(vector<T> &A) { for(T &x: A) read(x);};
inline void reads(char *s) {
int l=0;char c;
while(!valid(s[0]=getchar()));
while(valid(s[++l]=getchar()));
}
const int N = 2e5 + 10,M = 4e5 + 10;

struct edge{
    int u,v,w;
}edges[M];
int p[N];
int n,m;

int find(int x)
{
    if(p[x]!=x) p[x]=find(p[x]);
    return p[x];
}

void solve() {
    read(n,m);
    for(int i=0;i<m;i++){
        int u,v,w;
        read(u,v,w);
        edges[i]={u,v,w};
    }
	int ans = 0;
    for(int i=31;i>=0;i--){//枚举每一个数位
        int res = ans;
        for(int j=i-1;j>=0;j--) res|=(1<<j);//将比该位低的位全部置1,为后面选边进行准备,
        vector<PIII> v;
        for(int j=0;j<m;j++)//这是精华之处。进行选边,我们只保留满足高位要求,且当前位为0的边。
            if((edges[j].w|res)<=res)
                v.push_back({edges[j].w,{edges[j].u,edges[j].v}});
        int cnt = 0;
        for(int j=1;j<=n;j++) p[j]=j;
        for(auto x:v){
            int a = find(x.second.first),b = find(x.second.second),w = x.first;
            if(a!=b){
                p[a]=b;
                cnt++;//用来记录,对于该位来说,有多少条边在该位上为0;
                if(cnt==n-1) break;//若是已经够了则退出
            }
        }
        if(cnt<n-1) ans|=1<<i;//若是该位上为0的边数不够,则该位上为1.
    }
    cout<<ans<<endl;
}
 
int main() 
{
    int _;
    cin>>_;
 
    while(_ -- ) {
        solve();
    }
 
    return 0;
}
posted @ 2022-01-11 10:49  艾特玖  阅读(44)  评论(0)    收藏  举报