HDU 6368 2018HDU多校赛 第六场 Variance-MST(LCT+kruskal)

HDU 6368 2018HDU多校赛 第六场 Variance-MST(LCT+kruskal)_第1张图片

 

 

大致题意:给你很多条边,每条边有边权,现在让你找到一个方差最小的生成树,即最小方差生成树。

看到这题,我们首先回忆一下很久之前做过的一道,求最小极差生成树的题目 CSU 1845 。这套题目里面用到的主要思路就是,最小极差肯定是连续的一段,然后我就按照边的权值大小排序,每次看边是否可以直接加入,如果可以则直接加入,否则在加入这条边之后的环中寻找一条权值最小的边删掉。这样所有的边遍历一遍,中间过程中的极差最小就是我们的解。

接着,我们考虑这道题目,显然,这题的方差和极差是有关系的,因为方差越小意味着样本之间越接近,相应的极差可能也会更小,所以很自然而然的我们可以想到,最小方差生成树也一定是连续的一段。然后我们考虑一下暴力的做法如何去做。

方差当然也是与均值的取值相关的。对于两条相互冲突的边,其权值分别是wi和wj且满足wi

然后考虑优化。我们枚举M^2个均值显然还是太多了,有没有什么方法可以不枚举均值呢?我们发现,我们之前说的,最小方差生成树边权肯定是连续的一段,然后还要冲突的两条边的选取依据我们还没有怎么利用。其实,如果这么考虑,冲突的两条边只会是在按照边权kruskal过程中,冲突的边,同样我们删掉环中边权最小的边,这个删除的过程不正是相当于做了一次选择的过程吗?也即,在删除的时候,我是默认把均值调整到大于两边权值和的一半了,也就是说,我们在做kruskal的过程中,其实也是在枚举这个均值。

但是,这里会出现一个问题,如果说之前有一对边(i,j)矛盾我把它们删除了,当前又有边(o,p)矛盾,\large w_p>w_o,我也要删除,但有可能 \large (w_i+w_j)/2>(w_o+w_p)/2 。就是说在删掉(i,j)后我就默认把均值调整到大于这两条边权之和的一半了,那么此时o和p这一对应该早就已经选择p,但是在删除之前还是选择了o,这里就会出现错误。所以说,我们不妨按照这个两条矛盾边的边权和的一半排一个序。

具体来说,对于每一个一开始直接加入的边,我们把它的优先级设置为负无穷大,权值设置为边权。然后对于每一个与前面边矛盾的边,把它的边设置为 \large w_i+w_j ,这里i和j表示小的边和大的边,权值设置为大边的权值。同时对应要把前一个边删除的操作也要保存,优先级还是\large w_i+w_j,权值设置为前面小边权值的取反。最后,还要存下把所有的没有矛盾的边也删掉的操作,对应优先级为正无穷大,权值为对应边的权值的取反。

这样总共m条边,就会有2m个操作,对一个每条边的加入和删除。按照优先级排序之和,这个顺序就恰好对应了我们的均值从小到大枚举的过程。我们要做的就是依次处理这些操作,每次当发现加入的边条数为n-1的时候,就比较更新一下最后的结果。

关于这个最后结果的比较,我们用到了概率论里面给出的公式 \large D(x)=E(x^2)-E^2(x) 即平方和的均值减去和的均值的平方。所以我们比较的是分为两部分,一个是平方和,一个是和。最后是平方和除以(n-1),和的平方除以(n-1)的平方。我们通分一下,就是 (平方和-和的平方/n-1)) /(n-1)。为了比较最小我们选择比较分子,取最小的分子,而这个分子还有一个除法,这个除法也是不能利用逆元的,所以我学习标称利用了一种很骚的操作。具体见代码:

#include
#define N 300010
#define LL long long
#define mod 998244353
#define INF 0x3f3f3f3f
#define IO ios::sync_with_stdio(0);cin.tie(0)
#define file(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
using namespace std;
stack sta;

struct Edge
{
    int x,y,w;

    bool operator < (const Edge a) const
    {
        return w P;
std::vector

q; int con[N],n,m; struct Link_Cut_Tree { int son[N][2],fa[N],mn[N],val[N]; bool rev[N]; void init(int n) { for(int i=0;i<=n;i++) { son[i][0]=son[i][1]=0; fa[i]=0; } } inline bool which(int x){return son[fa[x]][1]==x;} bool isroot(int x){return !fa[x]||son[fa[x]][which(x)]!=x;} inline void push_up(int x) { if (!x) return; mn[x]=val[x]; if (son[x][0]) mn[x]=min(mn[x],mn[son[x][0]]); if (son[x][1]) mn[x]=min(mn[x],mn[son[x][1]]); } inline void Reverse(int x) { if (!x) return; swap(son[x][0],son[x][1]); rev[x]^=1; } inline void push_down(int x) { if (!x||!rev[x]) return; Reverse(son[x][0]); Reverse(son[x][1]); rev[x]=0; } inline void Rotate(int x) { int y=fa[x]; bool ch=which(x); son[y][ch]=son[x][ch^1];son[x][ch^1]=y; if (!isroot(y)) son[fa[y]][which(y)]=x; fa[x]=fa[y]; fa[y]=x; fa[son[y][ch]]=y; push_up(y); push_up(x); } inline void splay(int x) { int i=x; for(;!isroot(i);i=fa[i]) sta.push(i); sta.push(i); while (!sta.empty()) { push_down(sta.top()); sta.pop(); } while (!isroot(x)) { int y=fa[x]; if (!isroot(y)) { if (which(x)^which(y)) Rotate(x); else Rotate(y); } Rotate(x); } } inline void access(int x) { int y=0; while (x) { splay(x); son[x][1]=y; push_up(x); y=x; x=fa[x]; } } void beroot(int x){access(x);splay(x);Reverse(x);} inline int getroot(int x) { access(x); splay(x); while (son[x][0]) x=son[x][0]; return x; } inline void link(int x,int y) { beroot(x); fa[x]=y; } inline void del(int x) { splay(x); for(int i=0;i<2;i++) { if (!son[x][i]) continue; fa[son[x][i]]=fa[x]; fa[x]=son[x][i]=0; } } } LCT; int qpow(int x,int n) { int res=1; while(n) { if (n&1) res=(LL)res*x%mod; x=(LL)x*x%mod; n>>=1; } return res; } int main() { IO; file(1007); int T; cin>>T; while(T--) { cin>>n>>m; q.clear(); LCT.init(n+m+1); for(int i=1;i<=m;i++) { con[i]=0; int x,y,w; cin>>x>>y>>w; g[i]=Edge{x,y,w}; } sort(g+1,g+1+m); for(int i=1;i<=m;i++) { int x=g[i].x,y=g[i].y,w=g[i].w; if (LCT.getroot(x)==LCT.getroot(y)) { LCT.beroot(x); LCT.access(y); LCT.splay(y); int id=LCT.mn[y]; LCT.del(id+n); con[id]=i; q.push_back(P(w+g[id].w,w)); } else q.push_back(P(-INF,w)); LCT.val[x]=LCT.val[y]=INF; int e=i+n; LCT.val[e]=i; LCT.link(x,e); LCT.link(y,e); } for(int i=1;i<=m;i++) { int w=g[i].w; if (con[i]) q.push_back(P(w+g[con[i]].w,~w)); else q.push_back(P(INF,~w)); } int e=0; LL sum=0,sqsum=0; LL ex2=1e18,e2x=1e18; sort(q.begin(), q.end()); for(auto i:q) { LL b,c; b=i.second; if (b>=0) e++,c=b*b; else e--,b=-~b,c=-(b*b); sum+=b; sqsum+=c; if (e==n-1) { LL x=sqsum-sum/(n-1)*sum-sum%(n-1)*sum/(n-1); LL y=-sum%(n-1)*sum%(n-1); if (y<0) y+=n-1,x--; if (ex2+(e2x>y)>x) ex2=x,e2x=y; } } int inv=qpow(n-1,mod-2); cout<<(ex2+e2x*inv%mod)%mod*inv%mod<

 

你可能感兴趣的:(树/生成树相关,Link,Cut,Tree,---------Online,Judge--------,HDU,2018HDU多校赛)