Codeforces 1633 E. Spanning Tree Queries ——暴力,kruskal,思维

This way

题意:

给你一张无向带权图,每次给你一个w,然后图中每条边的权值-w后取绝对值。并构造最小生成树。你的答案异或上这棵树的总权值,问你最后答案是多少。

题解:

我把+1写在了括号里,导致我找了一个多小时的bug,后面改着改着发现答案变了,于是开始研究这一块哪里能够导致答案变化,最终找到了这个bug…要是在比赛的时候这估计就找不到了,还是不够仔细啊。

首先我看完这道题目就发现n,m的范围很小,并且时限给了4s,怕是可以从这方面操作,我甚至能for一遍1e8!
然后发现了c的值<=1e8?那似乎我可以从这方面找突破口,要是它给了5e8我就歇菜了,估计正解也不是我这种方法…
那么我们是否能枚举每个值,然后做kruskal?很明显不能每次做,但是我们可以在关键位置做,什么是关键位置?也就是树的形状可能发生改变的时候。对于两条权重为a,b的边,当他们的取与否发生交换的时候可能会在(a+b)/2或者(a+b)/2+1的时候。这个时候原本权值小的边就变成大的边了。那么在这种时候我们才需要做kruskal。时间复杂度呢? O ( N l o g N M 2 ) O(NlogNM^2) O(NlogNM2)反正不会超。
那么对于做了kruskal的时候,答案当然是当前算出来的值啊,那之后呢?
我们可以发现,假设当前枚举到的值为i,然后我们知道上一次做完kruskal之后所有取出来的边的权重,假设权重<=i的边有x个,总共有y条边。那么i变成i+1的时候,是否x条边权值+1,y-x条边权值-1?

#include<bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
#define ll long long
const int N=55,M=305;
struct Edge{
    int x,y, l,real;
    bool operator<(const Edge& a)const {
        return l<a.l;
    }
}e[M],a[M];
int fa[N];
int finds(int x){return x==fa[x]?fa[x]:fa[x]=finds(fa[x]);}
bool hav[100000005];
int change[M*M];
int n,m,x,y,v,all,w[N];
int cal(int a,int b){return max(a,b)-min(a,b);}
ll kruskal(int x){
    for(int i=1;i<=n;i++)fa[i]=i;
    ll ans=0;
    all=0;
    for(int i=1;i<=m;i++)
        e[i]={a[i].x,a[i].y,cal(a[i].l,x),a[i].l};
    sort(e+1,e+1+m);
    for(int i=1;i<=m;i++){
        int fx=finds(e[i].x),fy=finds(e[i].y);
        if(fx!=fy){
            fa[fy]=fx;
            ans+=e[i].l;
            w[++all]=e[i].real;
        }
    }
    sort(w+1,w+1+all);
    return ans;
}
int main()
{

    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].l);
    int tot=0;
    for(int i=1;i<=m;i++)
        for(int j=i+1;j<=m;j++)
            change[++tot]=(a[i].l+a[j].l)/2,change[++tot]=(a[i].l+a[j].l)/2+1;
    sort(change+1,change+1+tot);
    tot=unique(change+1,change+1+tot)-change-1;
    int p,k;
    ll c,a,b;
    scanf("%d%d%lld%lld%lld",&p,&k,&a,&b,&c);
    ll q;
    for(int i=1;i<=p;i++)scanf("%lld",&q),hav[q]^=1;
    for(int i=p+1;i<=k;i++)
        q=(q*a+b)%c,hav[q]^=1;
    ll now=1,ans=0,last=kruskal(0),pos=1;
    for(int i=0;i<c;i++){
        if(now<=tot && change[now]<=i){
            while(now<=tot&&change[now]<=i)now++;
            last=kruskal(i);
            pos=1;
        }
        if(hav[i])
            ans^=last;
        while(pos<=all&&w[pos]<=i)pos++;
        last+=pos-1-all+pos-1;
    }
    printf("%lld\n",ans);
    return 0;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值