国家集训队2018论文集《最小方差生成树》命题报告 学习笔记/生成树

国家集训队$2018$论文集《最小方差生成树》命题报告 学习笔记/生成树

这几天出的第$4$道论文题,总的来说,代码量适中$(500+)$,远远小于前天代码$(1300+),$论文可读性强,知识点基本掌握,是一道不可多得的不可做题,痛斥出题人$!$

题意大概是这样,给定一张图,求出一个价值最小的生成树

生成树的代价计算方式为生成树边权的方差

拿到这道题就去推式子,想搞出一个能够直接用式子表示权值直接生成树,结果失败,然后考虑计算方差最小,肯定是选的越接近越好,继续思考无果

这道题在考场上可以拿到$12+8$的子任务分数

暴力解法

这个$trick$就很妙.就是说每次枚举平均数,然后做一遍最小生成树,是能保证最优的还是最优

因为方差在平均值等于$u$时候取到最小,每个都是这样,感性理解的话,无论怎么偏,都不会更优

毕竟是论文题,我们需要挖掘算法的本质问题,我们现在的问题是,不一定是选最大的边好,也不一定是选最小的边好,那选相邻的一部分是不会更差的,显然离散程度会比选旁边的小

显然这里不能随便排序,一个边的边权因为和平均值有关,所以这个排序是不确定的

z这里观察一下,什么时候排名会变,平均值经过两数平均值的时候会改变,那么一共$m^2$个端点,那么任意两个端点内部是不会发生变化的,直接$m^2$个最小生成树就好了,复杂度$m^3log(m)$,这个过程是可以优化的,可以考虑用$LCT$调整最小生成树,那就用新的小边去替换大边,具体实现不是很会,大概也会了,无非是看边的排名有没有变化,然后加进去看一下是否更优,只不过使用的$LCT$维护形态,$O(m^2logm)$常数巨大...

引出另一道题$Topcoder \ SRM\ 611\ Ega\ itarianism2$

$N$个点的完全图求最小生成树,边多的时候,用$prim$是更优的,$O(n^6)[n<=20]$

那么对于原题,其实还能继续优化,我们在这已经有了一个$O(m^3logm)$的优秀算法(在这里不考虑$LCT$解法,毕竟常数好大,$splay$常数都$20+$了,还是过不了),我们考虑一下,是不是还有些东西没有充分利用,的确有一部分,区间之间转移的是不是不用每次重新跑最小生成树

这里给出结论$:$

这个结论挺强的,就是说一条边如果存在于生成树中,一定会出现在一段连续的区间的生成树里面,感性想一想,一个边什么时候会消失.他的排名发生变化了,或者其余的边发生变化了,导致被替代,放到二次函数就很显然了

引理

在确定$u$的情况下,如果边$i$在所有权值比他小的边都放进去之后两端点联通,那么就不在生成树里面,否则在生成树里面

这样的话,我们可以把函数图像全都画出来,然后找出$l_i,r_i$,怎么找

我们只需要找到平均值区间让他在里面就好了,枚举每条边,然后把边权按平均值排个序,表示变化点,然后左端点就是找一个最小的平均值使得这条边恰好不用放进去,就是把所有小的边都放进去,并查集维护连通性就好了,小的边怎么找,就是把左函数单独提取出来,找到所有交点,从大到小排,看加到哪个就不用这条边了,那么就找到了$l$,至于$r$是以一样的,为啥是从大到小排序,显然的,越近的话排名越小

我们现在得到了每个边左右区间,这个时候区间就少了,只有$m$个,然后计算每个区间的生成树就好$tql$,复杂度$O(m^2logm)$

复杂度瓶颈在于求$l_i,r_i,$可以考虑从小到大加边,这样是没问题的,以为这样保证了上面要求的按平均值排序,那么这个每个边该不该加进去就好判断了,加边然后$LCT$求最大生成树就好了,还是说这里是大常数

这个可以用一个结论优化一部分,边$i,j$,如果最大生成树$u_i,v_i$的最短边$j$,那么$l_i=r_j=(w_i+w_j)/2$,感性想一想函数图像就很显然了

中间有一个关于乘积最小生成树优化,然后没用就寄了

上面已经解决了$T=1$,那么$T=2$其实是对第一问扩展罢了

**写到这我已经自闭了**

首先我们求出了最小方差生成树,暴力的按照$T=1$的话,可以考虑在$[l_i,r_i]$外找个最小值,当然会$TLE$,当然正解只是对这个思路优化了一下

我们只需要求$n$条边的答案就好了

删掉了生成树的一条边,$l_i$变化的边一定在右边,$r_i$变化的边一定在左边

那么要求出变化的$l,r$,还是从小到大插入,替换掉的最小边就是$l_i=r_j=(w_i+w_j)/2$,复杂度$O(n^3/n^2logn+nm)$,高精度常数炸掉

继续考虑优化,我们中间有一步直接得到了$m$个区间

每次修改我们改变的边只有$n$个,那么$l_i,r_i$改变的也是$n$个,不知道为什么...

$Ans=\sigma^2\times N^2=s_2\times N-s_1^2$

由于我们范围已经确定了,那么这个东西就搞一个差分就好了,对于更改的位置呢

把原序列变化成了$n$个区间,我们这个东西的变化值有了,就是这一段$s$会发生的变化就知道了,我们就是求着每个区间的最小值就好了

$s_1$是和,$s_2$是平方和

$Ans=(s_{i2}+\det s_2)\times N-(s_{i1}^2+2\times s_{i1}\times\det s_1+\det^2s_1)$

这是个斜率优化式子,线段树维护凸壳然后指针查询就好了

 粘一下$60pts$代码,最后那个维护很麻烦,大坑未补.

现在$450$行,写完貌似得破$600$了...

#include<bits/stdc++.h>
#define base 1000000000
#define int long long
#define MAXM 100005
#define MAXN 305
using namespace std;

struct node
{
       int x,y,w;
       int id;
}rd[MAXM];
bool cmp(node a,node b)
{
     return a.w<b.w;
}
int n,m,T,vis0[MAXM],L0[MAXM],R0[MAXM];
struct Num{
    int p,len;
    int a[5];
    Num(int k=0){
        p=len=0;
        memset(a,0,sizeof(a));
        if (k<0)len=-1;
        else{
            while (k){
                a[len++]=k%base;
                k/=base;
            }
        }
    }
}Check,S1,S2,ans,z1[MAXM],z2[MAXM],dS1[MAXM<<1],dS2[MAXM<<1],Ans[MAXM];
namespace IO{
    int num[100];
    int x;
    char c;
    int read(){
        x=0,c=getchar();
        while ((c<'0')||(c>'9'))c=getchar();
        while ((c>='0')&&(c<='9')){
            x=x*10+c-'0';
            c=getchar();
        }
        return x;
    }
    void write(Num x,char c='\0'){
        if (x.len<0){
            putchar('-');
            putchar('1');
            putchar(c);
            return;
        }
        for(int i=0;i<x.len;i++)
            for(int j=0;j<9;j++){
                num[++num[0]]=x.a[i]%10;
                x.a[i]/=10;
            }
        while ((num[0])&&(!num[num[0]]))num[0]--;
        if (!num[0])putchar('0');
        while (num[0])putchar(num[num[0]--]+'0');
        putchar(c);
    }
};
vector<pair<long long,int> >v0;
namespace LCT
{
    int fa[MAXM<<1],val[MAXM<<1],vis[MAXM],Max[MAXM<<1],Min[MAXM<<1],st[MAXM<<1],ch[MAXM<<1][2];
    int tag[MAXM<<1];
    bool Nrt(int x)
    {
         int f=fa[x];
         if(ch[f][0]==x||ch[f][1]==x) return true;
         return false;
    }
    //一个点的所有儿子里面只有一个实边 
    //splay维护的是一棵深度递增的节点
    //显然是左边的时候上边的,右边的是下面的
    //那么显然的维护出来的是这个点到最深点的一条链
    void push_up(int x)
    {
         Min[x]=min(Min[ch[x][0]],Min[ch[x][1]]);
         Max[x]=max(Max[ch[x][0]],Max[ch[x][1]]);
         if(x>n)
         {
             Min[x]=min(Min[x],x-n);
             Max[x]=max(Max[x],x-n);
         }
         //其实这里只有边才有权值
         //那么边权就是大小了 
    }
    void push_poz(int x)
    {
         swap(ch[x][0],ch[x][1]);
         tag[x]^=1;
    }
    void push_down(int x)
    {
         if(tag[x])
         {
             if(ch[x][0]) push_poz(ch[x][0]);
             if(ch[x][1]) push_poz(ch[x][1]);
             tag[x]^=1;
         }
    }
    bool get_which(int x)
    {
         return ch[fa[x]][1]==x;
    }
    void rotate(int x)
    {
         int f=fa[x],g_fa=fa[f],k=(ch[f][1]==x),w=ch[x][!k];
         if(Nrt(f)) ch[g_fa][ch[g_fa][1]==f]=x;
         ch[x][!k]=f;
         ch[f][k]=w;
         if(w) fa[w]=f;
         f[fa]=x;
         fa[x]=g_fa;
         push_up(f);
         push_up(x);
    }
    void splay(int x)
    {
         int y=x,z=0;
         st[++z]=y;
         while(Nrt(y)) st[++z]=y=fa[y];
         while(z) push_down(st[z--]);
         while(Nrt(x))
         {
               y=fa[x];z=fa[y];
               if(Nrt(y))
               {
                    rotate(get_which(x)==get_which(y)?y:x);
               }
               rotate(x);
         }
         push_up(x);
    }
    void access(int x)
    {
         for(int y=0;x;x=fa[y=x])
         {
              splay(x);
              ch[x][1]=y;
              push_up(x);
         }
    }
    void mkrt(int x)
    {
         access(x);
         splay(x);
         push_poz(x);
    }
    int Fdrt(int x)
    {
        access(x);
        splay(x);
        while(ch[x][0])
        {
             push_down(x);
             x=ch[x][0];
        }
        splay(x);
        return x;
    }
    void spilt(int x,int y)
    {
         mkrt(x);
         access(y);
         splay(y);     
    }
    void link(int x,int y)
    {
         mkrt(x);
         if(Fdrt(y)!=x)
         {
             fa[x]=y;
         }
    }
    void cut(int x,int y)
    {
         mkrt(x);
         if(Fdrt(y)==x&&fa[y]==x&&!ch[y][0])
         {
              fa[y]=ch[x][1]=0;
              push_up(x);
         }
    }
    int query_min(int x,int y)
    {
        mkrt(x);
        if(Fdrt(y)!=x) return 0;
        return Min[x];
    }
    int query_max(int x,int y)
    {
        mkrt(x);
        if(Fdrt(y)!=x) return 0;
        return Max[x];
    }
    int add_min(int id)
    {
        int pos=query_min(rd[id].y,id+n);
        //反正是加边,一开始先加一半的生成树不会变化
        //显然不会加半个边,那么现在加另一半就好了
        //查另一半,pos就是更新完最小生成树的位置 
        //如果有的话就更新就好了
        //由于维护的是最大生成树
        //感觉是最大生成树其实是最小的位置
        //反正都需要删掉hhh 
//        cout<<"pos: "<<pos<<endl;
        if(pos)
        {
           vis[0]--,vis[pos]=0;
           cut(rd[pos].y,pos+n);
        }
        vis[0]++,vis[id]=1;
        link(rd[id].y,id+n);
        return pos;
    }
    int add_max(int id)
    {
        int pos=query_max(rd[id].y,id+n);
        if(pos)
        {
           vis[0]--,vis[pos]=0;
           cut(rd[pos].y,pos+n);
        }
        vis[0]++,vis[id]=1;
        link(rd[id].y,id+n);
        return pos;
    }
    void Init()
    {
         Min[0]=0x3f3f3f3f,Max[0]=0; 
         //这就是LCT最小生成树的过程 
         for(int i=1;i<=n+m;i++)
         {
              fa[i]=tag[i]=ch[i][0]=ch[i][1]=0;
              push_up(i);
         }
         for(int i=1;i<=m;i++)
         {
              link(rd[i].x,i+n);
         }
    }
    void clear()
    {
         for(int i=1;i<=m;i++)
         {
              if(vis[i])
              {
                  cut(rd[i].y,i+n);
                  vis[0]--,vis[i]=0;
              }
         }
         //至今尚未知晓vis何用
         //vis应该是这条边在不在最小生成树里面 
    }
}
namespace Clac
{
    int cmp(Num x,Num y){
        if (x.len!=y.len){
            if (x.len<y.len)return -1;
            return 1;
        }
        for(int i=x.len-1;i>=0;i--)
            if (x.a[i]!=y.a[i]){
                if (x.a[i]<y.a[i])return -1;
                return 1;
            }
        return 0;
    }
    Num min(Num x,Num y){
        if (cmp(x,y)<0)return x;
        return y;
    }
    Num add(Num x,Num y){
        Num ans;
        if (x.p==y.p){
            ans.p=x.p,ans.len=max(x.len,y.len);
            for(int i=0;i<ans.len;i++){
                ans.a[i]+=x.a[i]+y.a[i];
                if (ans.a[i]>=base){
                    ans.a[i]-=base;
                    ans.a[i+1]++;
                }
            }
            if ((ans.len<5)&&(ans.a[ans.len]))ans.len++;
            return ans;
        }
        if (cmp(x,y)<0)swap(x,y);
        ans.p=x.p,ans.len=x.len;
        for(int i=0;i<ans.len;i++){
            ans.a[i]+=x.a[i]-y.a[i];
            if (ans.a[i]<0){
                ans.a[i]+=base;
                ans.a[i+1]--;
            }
        }
        while ((ans.len)&&(!ans.a[ans.len-1]))ans.len--;
        return ans;
    }
    Num dec(Num x,Num y){
        y.p^=1;
        return add(x,y);
    }
    Num mul(Num x,Num y){
        Num ans;
        ans.p=(x.p^y.p),ans.len=x.len+y.len-1;
        for(int i=0;i<x.len;i++)
            for(int j=0;j<y.len;j++){
                ans.a[i+j]+=x.a[i]*y.a[j];
                ans.a[i+j+1]+=ans.a[i+j]/base;
                ans.a[i+j]%=base;
            }
        if ((ans.len<5)&&(ans.a[ans.len]))ans.len++;
        return ans;
    }
};//-------------------封装高精度 
void get_LR()
{
     memset(L0,0,sizeof(L0));
     memset(R0,0,sizeof(R0));
     LCT::clear();
     for(int i=1;i<=m;i++)
     {
          L0[i]=LCT::add_min(i);
//          cout<<"L0: "<<L0[i]<<endl;
          if(L0[i]) R0[L0[i]]=i;
     }
}
void get_v()
{
     v0.clear();
     for(int i=1;i<=m;i++)
     {
          if(!L0[i])
          {
               v0.push_back(make_pair(0,i));
          }
          else
          {
               v0.push_back(make_pair((rd[L0[i]].w+rd[i].w<<1)+1,i));
          }
          if(R0[i])
          {
              v0.push_back(make_pair((rd[i].w+rd[R0[i]].w<<1)+1,-i));
          }
     }
}
void clac()
{
     //果不其然,这里要排序
      //这是就是统计贡献了 
     sort(v0.begin(),v0.end());
     int tot=0;
     S1=S2=0,ans.len=10;
     for(int i=0;i<v0.size();i++)
     {
          int pos=abs(v0[i].second);
          if(v0[i].second>0)
          {
              tot++;
              S1=Clac::add(S1,z1[pos]),S2=Clac::add(S2,z2[pos]);
          }
         else 
         {
             tot--;
             S1=Clac::dec(S1,z1[pos]),S2=Clac::dec(S2,z2[pos]);
         }
         if(v0[i].first!=v0[i+1].first)
         {
             if(!v0[i].first) Check=S1;
             ans=Clac::min(ans,Clac::dec(S2,Clac::mul(S1,S1)));
         }
     }
//     cout<<"tot: "<<tot<<endl;
     if(tot!=n-1)
     {
        ans=-1;
        return ;    
     }
     S1=S2=0;
     memset(vis0,0,sizeof(vis0));
     for(int i=0;i<v0.size();i++)
     {
          int pos=abs(v0[i].second);
          vis0[pos]^=1;
         if(v0[i].second>0)
          {
              S1=Clac::add(S1,z1[pos]),S2=Clac::add(S2,z2[pos]);
          }
         else 
         {
             S1=Clac::dec(S1,z1[pos]),S2=Clac::dec(S2,z2[pos]);
         }
         if(v0[i].first!=v0[i+1].first&&(!Clac::cmp(ans,Clac::dec(S2,Clac::mul(S1,S1)))))break;
     }
}
signed main()
{
    cin>>n>>m>>T;
    for(int i=1;i<=m;i++)
    {
        cin>>rd[i].x>>rd[i].y>>rd[i].w;
        rd[i].id=i;
    }
    sort(rd+1,rd+1+m,cmp);
    LCT::Init();
    for(int i=1;i<=m;i++)
    {
        z1[i]=rd[i].w;
        z2[i]=Clac::mul(n-1,Clac::mul(z1[i],z1[i]));
//        IO::write(z1[i],'\n');
//        IO::write(z2[i],'\n');
    }
    get_LR();
//    cout<<"VIS: "<<LCT::vis[0]<<endl;
    if(LCT::vis[0]!=n-1)
    {
       if(T==1) 
       {
              puts("-1");
       }
       else
       {
              for(int i=1;i<=m;i++)
              {
                     puts("-1");
              }
       }
       return 0;
    }
    get_v(),clac();
    if(T==1)
    {
        IO::write(ans,'\n');
        return 0;
    }
    for(int i=1;i<=m;i++)
    {
        if(!vis0[i]) Ans[rd[i].id]=ans;    
    }
    
}

 机房dalao教我压行...

#include<bits/stdc++.h>
#define base 1000000000
#define int long long
#define MAXM 100005
#define MAXN 305
using namespace std;
struct node{int x,y,w,id;}rd[MAXM];
bool cmp(node a,node b){return a.w<b.w;}
int n,m,T,vis0[MAXM],L0[MAXM],R0[MAXM];
struct Num{int p,len;int a[5];Num(int k=0){p=len=0;memset(a,0,sizeof(a));if (k<0)len=-1;else{while (k){    a[len++]=k%base;k/=base;}}}}Check,S1,S2,ans,z1[MAXM],z2[MAXM],dS1[MAXM<<1],dS2[MAXM<<1],Ans[MAXM];
namespace IO{
    int num[100],x;
    char c;
    int read(){x=0,c=getchar();while ((c<'0')||(c>'9'))c=getchar();while ((c>='0')&&(c<='9')){x=x*10+c-'0';c=getchar();}return x;}
    void write(Num x,char c='\0'){if (x.len<0){putchar('-');putchar('1');putchar(c);return;}
        for(int i=0;i<x.len;i++)for(int j=0;j<9;j++){num[++num[0]]=x.a[i]%10;x.a[i]/=10;}
        while ((num[0])&&(!num[num[0]]))num[0]--;if (!num[0])putchar('0');while (num[0])putchar(num[num[0]--]+'0');putchar(c);}
};
vector<pair<long long,int> >v0;
namespace LCT
{
    int fa[MAXM<<1],val[MAXM<<1],vis[MAXM],Max[MAXM<<1],Min[MAXM<<1],st[MAXM<<1],ch[MAXM<<1][2],tag[MAXM<<1];
    bool Nrt(int x){int f=fa[x];if(ch[f][0]==x||ch[f][1]==x) return true; return false;}
    void push_up(int x){Min[x]=min(Min[ch[x][0]],Min[ch[x][1]]);Max[x]=max(Max[ch[x][0]],Max[ch[x][1]]);if(x>n){Min[x]=min(Min[x],x-n);Max[x]=max(Max[x],x-n);}}
    void push_poz(int x){swap(ch[x][0],ch[x][1]);tag[x]^=1;}
    void push_down(int x){if(tag[x]){if(ch[x][0]) push_poz(ch[x][0]);if(ch[x][1]) push_poz(ch[x][1]);tag[x]^=1;}}
    bool get_which(int x){return ch[fa[x]][1]==x;}
    void rotate(int x){int f=fa[x],g_fa=fa[f],k=(ch[f][1]==x),w=ch[x][!k];if(Nrt(f)) ch[g_fa][ch[g_fa][1]==f]=x;ch[x][!k]=f;ch[f][k]=w;if(w) fa[w]=f;f[fa]=x;fa[x]=g_fa;push_up(f);push_up(x);}
    void splay(int x){int y=x,z=0;st[++z]=y;while(Nrt(y)) st[++z]=y=fa[y];while(z) push_down(st[z--]);while(Nrt(x)){y=fa[x];z=fa[y];if(Nrt(y)){rotate(get_which(x)==get_which(y)?y:x);}rotate(x);}push_up(x);}
    void access(int x){for(int y=0;x;x=fa[y=x]){splay(x);ch[x][1]=y;push_up(x);}}
    void mkrt(int x){access(x);splay(x);push_poz(x);}
    int Fdrt(int x){access(x);splay(x);while(ch[x][0]){push_down(x);x=ch[x][0];}splay(x);return x;}
    void spilt(int x,int y){mkrt(x);access(y);splay(y);}
    void link(int x,int y){mkrt(x);if(Fdrt(y)!=x){fa[x]=y;}}
    void cut(int x,int y){mkrt(x);if(Fdrt(y)==x&&fa[y]==x&&!ch[y][0]){fa[y]=ch[x][1]=0;push_up(x);}}
    int query_min(int x,int y){mkrt(x);if(Fdrt(y)!=x) return 0;return Min[x];}
    int query_max(int x,int y){mkrt(x);if(Fdrt(y)!=x) return 0;return Max[x];}
    int add_min(int id){int pos=query_min(rd[id].y,id+n);if(pos){vis[0]--,vis[pos]=0;cut(rd[pos].y,pos+n);}vis[0]++,vis[id]=1;link(rd[id].y,id+n);return pos;}
    int add_max(int id){int pos=query_max(rd[id].y,id+n);if(pos){vis[0]--,vis[pos]=0;cut(rd[pos].y,pos+n);}vis[0]++,vis[id]=1;link(rd[id].y,id+n);return pos;}
    void Init(){Min[0]=0x3f3f3f3f,Max[0]=0;for(int i=1;i<=n+m;i++){fa[i]=tag[i]=ch[i][0]=ch[i][1]=0;push_up(i);}for(int i=1;i<=m;i++){link(rd[i].x,i+n);}}
    void clear(){for(int i=1;i<=m;i++){if(vis[i]){cut(rd[i].y,i+n);vis[0]--,vis[i]=0;}}}
}
namespace Clac{
    int cmp(Num x,Num y){if (x.len!=y.len){if (x.len<y.len)return -1;return 1;}for(int i=x.len-1;i>=0;i--)if (x.a[i]!=y.a[i]){if (x.a[i]<y.a[i])return -1;return 1;}return 0;}
    Num min(Num x,Num y){if (cmp(x,y)<0)return x;return y;}
    Num add(Num x,Num y){Num ans;if (x.p==y.p){ans.p=x.p,ans.len=max(x.len,y.len);for(int i=0;i<ans.len;i++){ans.a[i]+=x.a[i]+y.a[i];if (ans.a[i]>=base){ans.a[i]-=base;ans.a[i+1]++;}}if ((ans.len<5)&&(ans.a[ans.len]))ans.len++;return ans;}if (cmp(x,y)<0)swap(x,y);ans.p=x.p,ans.len=x.len;for(int i=0;i<ans.len;i++){ans.a[i]+=x.a[i]-y.a[i];if (ans.a[i]<0){ans.a[i]+=base;ans.a[i+1]--;}}while ((ans.len)&&(!ans.a[ans.len-1]))ans.len--;return ans;}
    Num dec(Num x,Num y){y.p^=1;return add(x,y);}
    Num mul(Num x,Num y){Num ans;ans.p=(x.p^y.p),ans.len=x.len+y.len-1;for(int i=0;i<x.len;i++)for(int j=0;j<y.len;j++){ans.a[i+j]+=x.a[i]*y.a[j];ans.a[i+j+1]+=ans.a[i+j]/base;ans.a[i+j]%=base;}if ((ans.len<5)&&(ans.a[ans.len]))ans.len++;return ans;}
};//-------------------封装高精度 
void get_LR(){memset(L0,0,sizeof(L0));memset(R0,0,sizeof(R0));LCT::clear();for(int i=1;i<=m;i++){L0[i]=LCT::add_min(i);if(L0[i]) R0[L0[i]]=i;}}
void get_v(){v0.clear();for(int i=1;i<=m;i++){if(!L0[i]){v0.push_back(make_pair(0,i));}else{v0.push_back(make_pair((rd[L0[i]].w+rd[i].w<<1)+1,i));}if(R0[i]){v0.push_back(make_pair((rd[i].w+rd[R0[i]].w<<1)+1,-i));}}}
void clac()
{
     sort(v0.begin(),v0.end());int tot=0;S1=S2=0,ans.len=10;
     for(int i=0;i<v0.size();i++){int pos=abs(v0[i].second);if(v0[i].second>0){tot++;S1=Clac::add(S1,z1[pos]),S2=Clac::add(S2,z2[pos]);}else{tot--;S1=Clac::dec(S1,z1[pos]),S2=Clac::dec(S2,z2[pos]);}if(v0[i].first!=v0[i+1].first){if(!v0[i].first) Check=S1;ans=Clac::min(ans,Clac::dec(S2,Clac::mul(S1,S1)));}}
     if(tot!=n-1){ans=-1;return ;}
     S1=S2=0;memset(vis0,0,sizeof(vis0));
     for(int i=0;i<v0.size();i++){int pos=abs(v0[i].second);vis0[pos]^=1;if(v0[i].second>0){S1=Clac::add(S1,z1[pos]),S2=Clac::add(S2,z2[pos]);}else{S1=Clac::dec(S1,z1[pos]),S2=Clac::dec(S2,z2[pos]);}if(v0[i].first!=v0[i+1].first&&(!Clac::cmp(ans,Clac::dec(S2,Clac::mul(S1,S1)))))break;}
}
signed main(){
    cin>>n>>m>>T;
    for(int i=1;i<=m;i++){cin>>rd[i].x>>rd[i].y>>rd[i].w;rd[i].id=i;}sort(rd+1,rd+1+m,cmp);LCT::Init();
    for(int i=1;i<=m;i++){z1[i]=rd[i].w;z2[i]=Clac::mul(n-1,Clac::mul(z1[i],z1[i]));}get_LR();
    if(LCT::vis[0]!=n-1){if(T==1){puts("-1");}else{for(int i=1;i<=m;i++){puts("-1");}}return 0;}
    get_v(),clac();if(T==1){IO::write(ans,'\n');return 0;}
}

 

posted @ 2022-03-13 10:24  Authentic_k  阅读(99)  评论(4编辑  收藏  举报