并查集——codevs 1188City战争

来源:互联网 发布:linux复制所有文件 编辑:程序博客网 时间:2024/06/14 03:52

http://codevs.cn/problem/1188/
我靠我不会;
fop_zz教我了半天我才会的;
首先n*n的暴力就是每条线段从两个点dfs;
然后把两个点的值乘起来*2;
这就是一条的值了;
我们在考虑权值不同的情况;
我们只要把边从小到大排序然后用并查集维护就好了;
但是如果边权有相同的情况就不好搞了;

有一个想法;
就是我们同一个权值的边一起加;
对于一条边,加之前其一段的点数是a,加之后是总共点数b;
那么值就是(b-a)*a*2;
这就有问题了,我们并查集不可以存点数了;
因为并查集在不断变化;
那我们只好把点数存在点上了;
这个时候我们要先搞一个dfs树;
记录每个节点的深度;
而并查集维护的就是这个联通快的点数放在哪里;
其实全放在联通快深度最高点;
我们合并两个并查集的时候,就要把低节点合并到高节点去;
比如x-y这一线段
x深度浅
那么get(y)一定是y
我们要把
fa[y]=get(x)
并且把点数合并过去;
于是y~get(x)的点的点数全要加x的点数;
因为我们要计算答案的;
仅仅把get(x)的权值加上y是不够的;

#include<bits/stdc++.h>#define Ll long longusing namespace std;const int N=1e5+5;struct cs{int v,to,nxt;}a[N*2];struct in{int x,y,z,num;}I[N];struct dian{int deep,fa,size;}d[N];int fa[N];int head[N],ll;int an[N],top;Ll ans;int n,m,l,r,x,y,z;void init(int x,int y,int z){    a[++ll].to=y;    a[ll].v=z;    a[ll].nxt=head[x];    head[x]=ll;}void dfs(int x,int y,int z){    d[x].deep=z; d[x].fa=y; d[x].size=1; fa[x]=x;    for(int k=head[x];k;k=a[k].nxt)        if(a[k].to!=y)dfs(a[k].to,x,z+1);}bool cmp(in a,in b){return a.z<b.z;}int get(int x){return fa[x]==x?x:fa[x]=get(fa[x]);}void insert(int x,int y){    int yy=get(y);    fa[x]=yy;    for(int k=d[x].fa;k!=yy;k=d[k].fa)d[k].size+=d[x].size;    d[yy].size+=d[x].size;  }int main(){    freopen("City.in","r",stdin);    freopen("City.out","w",stdout);    scanf("%d",&n);n--;    for(int i=1;i<=n;i++){        scanf("%d%d%d",&x,&y,&z);        I[i].x=x; I[i].y=y; I[i].z=z; I[i].num=i;        init(x,y,z);init(y,x,z);    }    dfs(1,-1,1);    sort(I+1,I+n+1,cmp);    l=1;r=1;    while(l<=n){        while(r+1<=n&&I[r+1].z==I[r].z)r++;        for(int i=l;i<=r;i++){            x=I[i].x; y=I[i].y;            if(d[x].deep>d[y].deep)insert(x,y);else insert(y,x);        }        for(int i=l;i<=r;i++){            x=I[i].x; y=I[i].y;            if(d[x].deep>d[y].deep){                y=d[get(y)].size;                x=d[x].size;                            }else{                y=d[y].size;                x=d[get(x)].size;                swap(x,y);            }            Ll temp=(Ll)(y-x)*x;                if(ans==temp)an[++top]=I[i].num;                if(ans<temp)ans=temp,an[top=1]=I[i].num;        }        l=r+1;r=l;    }    sort(an+1,an+top+1);    printf("%lld %d\n",ans*2,top);    for(int i=1;i<=top;i++)printf("%d ",an[i]);}

其实这种写法会被卡掉;
我们搞一条链的话

for(int k=d[x].fa;k!=yy;k=d[k].fa)d[k].size+=d[x].size;

这里会萎;
但是如果我们把同权值的线段先排序;
这就好啦