C造桥与砍树

链接

题意:

有n个带权的点以及参数k,要求生成一个最小生成树,每个点之间的边权为两个点权之和模k的结果

思路:

对所有权值模k后

发现对于一个权值为val的结点u,链接它的最优结点是 现在还没进入生成树的 (权值最小)或者(最小的 权值大于等于k-val) 的节点

因此贪心去选择这些边,然而有可能从u出发有不止两条的最优连边,因此在优先队列取出某个结点后,还需要对其前缀结点再次进行连边操作

int n,k;
const int M=1e5+5;
pii a[M];
struct node{
    int w;
    int val;
    int u;
    int pre;
    int pre_val;
    bool operator<(const node&a)const{
        return w>a.w;
    }
    node(){}
    node(int a,int b,int c,int p,int z){
        w=a;
        val=b;
        u=c;
        pre=p;
        pre_val=z;
    }
};
void solve(){
    cin>>n>>k;
    rep(i,1,n){
        cin>>a[i].fi;a[i].fi%=k;
        a[i].se=i;
    }
    sort(a+1,a+1+n);
    int ans=0;
    priority_queue<node>pq;
    pq.push(node(0,a[1].fi,a[1].se,0,0));
    set<pii>st;
    rep(i,1,n)st.insert({a[i].fi,a[i].se});

    // vector<pii>vec;
    vector<int>vis(n+1);
    while(pq.size()){
        auto[w,val,u,pre,pre_val]=pq.top();pq.pop();
        if(vis[u])continue;
        
        vis[u]=1;
        st.erase({val,u});
        ans+=w;
        if(st.size()==0)break;
        // vec.pb({u,val});


            pii X=(*st.begin());
            pq.push(node((val+X.fi)%k,X.fi,X.se,u,val));
            auto it=(st.lower_bound({k-val,0}));
            if(it!=st.end()){
                X=(*it);
                pq.push(node((val+X.fi)%k,X.fi,X.se,u,val));
            }

            if(pre){
                X=(*st.begin());
                pq.push(node((pre_val+X.fi)%k,X.fi,X.se,pre,pre_val));
                auto it=(st.lower_bound({k-pre_val,0}));
                if(it!=st.end()){
                    X=(*it);
                    pq.push(node((pre_val+X.fi)%k,X.fi,X.se,pre,pre_val));
                }
            }
    }
    cout<<ans<<endl;
}
posted @ 2025-09-24 13:36  Marinaco  阅读(34)  评论(0)    收藏  举报
//雪花飘落效果