[loj2469]最小方差生成树

2018年论文题

约定:令点集V=[1,n]、边集E=[1,m],记m条边依次为ei=(xi,yi,ci)(其中1im),将其按照ci从小到大排序,即不妨假设有c1c2...cm


 先来考虑T=1的情况,即如何求最小方差生成树

题意即求minETE,ETxET(μcx)2n1(其中μ=xETcxn1

考虑函数f(x)=i=1n1(xai)2n1,根据二次函数的性质其最小值恰在x=i=1n1ain1处取到

换言之,有xET(μcx)2n1=minμRxET(μcx)2n1

代入后交换顺序,即minμRminETE,ETxET(μcx)2n1,其中分子即以(μci)2为边权求最小生成树


 令Eχ表示μ=χ时最小生成树的边集,为了使其能被唯一确定,求最小生成树时将ei按照(μci)2i这两个关键字从小到大排序(假设用kruskal求最小生成树)

将问题从边的角度来考虑,显然iEχ当且仅当μ=χ时排在ei前面的边不能使xiyi连通

不妨假设χci,那么ej排在ei前面当且仅当2χcicj<cicj=cij<i

显然χ的范围具有单调性,即只需要不断加入ei1,ei2,...直至xiyi连通,设最后一条边加入的是ek,那么满足χciiEχχ的范围为ci+ck2<χci

(特别的,若最后仍未连通即令ck=


 显然,使用LCT维护i之前的边(不包括i)关于编号的最大生成树,询问的也即该生成树从xiyi路径上的编号最小的边,时间复杂度为o(mlogm)

关于χ>ciiEχ的范围,可以类似地求出,但会多一个2的常数,实际上可以避免

li为最大的k满足e[k,i)能使xiyi连通,ri为最小的k满足e(i,k]能使xiyi连通

结论:Li存在,则RLi=i;若Ri存在,则RLi=i

k=Li,则e[k,i)能使xiyi连通,那么e(k,i]即能使xkyk连通,也即Rki

另一方面,如果e(k,i)就能使xkyk连通,那么e[k,i)e(k,i)的连通性应该相同(因为(xk,yk)不影响连通性),也即li可以为k+1,与Li的最大性矛盾,因此Rki

综上,即有Rk=i,类似地也可以得到后者

由此即可线性求出RiLi之前已求出),根据后半部分,未被覆盖的Ri即为无解


 进而将两个范围求并,即得到满足iEχχ的范围为(cLi+ci2,ci+cRi2]

此时,Eχ即对应范围包含χi所组成的集合(注意这个范围是充分必要的),那么只需要通过离散和差分对每一个位置维护S1=ciS2=ci2的和即可(方差即(n1)S2S12(n1)2

另外,需要考虑实数的位置如何处理,可以将权值乘2并用两整数中间的部分代替该段实数

时间复杂度为o(mlogm),可以通过


 下面考虑T=2的情况,即如何对删去每一条边后的图求最小方差生成树

通过T=1时的做法,即可在o(mlogm)的时间内求出最小方差生成树即其方案

显然删除方案以外的边是不影响答案的,因此只需要考虑方案中的n1条边,那么对这n1条边暴力删除并再求一次最小方差生成树,即得到一个o(nmlogm)的做法,但无法通过

假设删除的边是edel,那么Li发生变化的边必然都在e(del,m]这些边关于编号的最小生成树上,因为如果ei不在最小生成树上,即等价于e(del,i)能使xiyi连通,显然删去edel没有意义

显然这样的边只有o(n)条,但求最大生成树仍要从前往后依次加边,复杂度并没有优化


 记T1e[1,del)的关于编号的最大生成树,T2e(del,m]的关于编号的最小生成树

初始令T=T2,并将T1中的边的按编号从大到小依次加入T,并继续维护T为最小生成树

结论:假设在加入ei时删除了ej,则有Lj=i

显然xjyj路径上所有边编号都在[i,j)中,因此也即Lji

同时如果Lj>i,那么不难得到xiyi可以通过e(i,del)连通,与eiT1上矛盾

通俗的来说,求e[1,i)的最大生成树上在edel之前的边一定在e[1,del)的最大生成树上,并且edel之后的边一定不会作为li,因此只关心于这类边的连通性

另外,对于T1中最终仍没有被删除的边ei,则Li无解

此时,重新计算LiRi的时间复杂度即降为o(n2logm)


 下面,给出一些关于实现上的细节:

1.关于LCT的清空,可以维护一个当前的边集(用标记数组即可),那么清空时遍历所有边并用删掉即可,由于删除的复杂度与加入时相同,因此相当于仅为o(m),总复杂度也即o(nm)

2.关于T1T2需要在初始预处理,在求最小/最大生成树过程中,当访问到的边是在方案中时,就将当前LCT中维护的边集o(m)找出即可,总复杂度也为o(nm)

3.关于离散和差分,重新暴力排序+二分复杂度又会退化为o(nmlogm),注意到总共只会额外产生o(n2)个位置,将这些位置预处理出来并排序,然后将初始的差分数组记录

此时,每一次即对差分数组的o(n)个位置修改,总复杂度也即o(mlogm+n2logm+nm)

时间复杂度为o(mlogm+n2logm+nm),可以通过


 然而,注意到答案的范围为n2C2,而在子任务8该值达到了1041的级别,无法使用__int128存储

由此,即需要在差分的过程中使用高精度乘法求S12(显然其他都不需要高精度),设高精度的常数为o(P),时间复杂度即变为o(mlogn+n2logm+Pnm),无法通过

下面,具体的来描述差分的过程——

N=n2+m,差分数组即是两个长度为N的序列,初始为分别为ΔS1(i)ΔS2(i)(下标为[1,N]

每一次查询,修改其中o(n)​个位置的值(无后效性),并查询
min1iN((n1)j=1iΔS2(j)(j=1iΔS1(i))2)
(其余部分的复杂度显然都可以做到o(mlogm+n2logm+nm+Pn2),其中o(Pn2)为计算修改的值)


 预处理出前缀和S1(i)=j=1iΔS1(j),S2(i)=j=1iΔS2(j)(对于初始状态),每一次修改的o(n)个位置即将原序列划分为o(n)段,并对每一段[l,r]分别求出i[l,r]的最小值

(其中[l,r]不包含修改的位置,修改的位置直接暴力o(Pn2)计算即可)

对于[l,r]​,求出此次修改对i[l,r]​的S1(i)​和S2(i)​的变化量,分别记作Δs1​和Δs2​(显然对所有位置都相同),那么i[l,r]​的最小值即
mini=lr((n1)(S2(i)+Δs2)(S1(i)+Δs1)2)
并对于其中一段[l,r],求出其之前的本次修改的变化量Δs1Δs2

简单化简,即
mini=lr(2Δs1S1(i)+((n1)S2(i)S12(i)))+((n1)Δs2Δs12)


 此时,问题即可以看作求经过(2S1(i),(n1)S2(i)S12(i))且斜率为Δs1的直线的最小截距

求出[l,r]中的点所构成的下凸壳,二分找到其中第一个斜率大于等于Δs1的线段的左端点即为最小值

可以使用线段树来维护,预处理时先将所有点按照x坐标排序,并依次加入线段树上区间包含其的o(logN)个凸包中,那么预处理的时间复杂度即为o(PNlogN)

查询时将[l,r]划分为o(logN)个区间,再在每一个区间对应的线段树凸包上二分,将所有最小值取min即可,那么查询的时间复杂度为o(Pn2log2N)

进一步的,可以将线段树每一个区间对应的凸包上的询问离线并排序,再利用单调性做到线性即可

关于排序,再将所有Δs1排序再依次加入,注意到外部仅有o(n2)个,因此查询复杂度降为o(Pn2logN)

时间复杂度为o(nm+PNlogN),可以通过

(代码只优化到o(mlogm+n2logm+Pnm),但已经可以通过)

复制代码
  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define N 305
  4 #define M 100005
  5 #define base 1000000000
  6 #define ll long long
  7 #define pli pair<ll,int>
  8 #define fi first
  9 #define se second
 10 struct Data{
 11     int x,y,id;
 12     ll z;
 13     bool operator < (const Data &k)const{
 14         return z<k.z;
 15     }
 16 }e[M];
 17 struct Num{
 18     int p,len;
 19     ll a[5];
 20     Num(ll k=0){
 21         p=(k<0),len=0;
 22         memset(a,0,sizeof(a));
 23         k=abs(k);
 24         while (k){
 25             a[len++]=k%base;
 26             k/=base;
 27         }
 28     }
 29 }Check,S1,S2,ans,z1[M],z2[M],dS1[M<<1],dS2[M<<1],Ans[M];
 30 vector<int>T1[M],T2[M];
 31 vector<pli>v0,v[M];
 32 int n,m,T,q,L0[M],R0[M],L[M],R[M],vis0[M];
 33 ll Pos[M<<1];
 34 namespace IO{
 35     int num[100];
 36     ll x;
 37     char c;
 38     ll read(){
 39         x=0,c=getchar();
 40         while ((c<'0')||(c>'9'))c=getchar();
 41         while ((c>='0')&&(c<='9')){
 42             x=x*10+c-'0';
 43             c=getchar();
 44         }
 45         return x;
 46     }
 47     void write(Num x,char c='\0'){
 48         if (x.p)putchar('-');
 49         for(int i=0;i<x.len;i++)
 50             for(int j=0;j<9;j++){
 51                 num[++num[0]]=x.a[i]%10;
 52                 x.a[i]/=10;
 53             }
 54         while ((num[0])&&(!num[num[0]]))num[0]--;
 55         if (!num[0])putchar('0');
 56         while (num[0])putchar(num[num[0]--]+'0');
 57         putchar(c);
 58     }
 59 };
 60 namespace Calc{
 61     int cmp(Num x,Num y){
 62         if (x.len!=y.len){
 63             if (x.len<y.len)return -1;
 64             return 1;
 65         }
 66         for(int i=x.len-1;i>=0;i--)
 67             if (x.a[i]!=y.a[i]){
 68                 if (x.a[i]<y.a[i])return -1;
 69                 return 1;
 70             }
 71         return 0;
 72     }
 73     Num min(Num x,Num y){
 74         if (cmp(x,y)<0)return x;
 75         return y;
 76     }
 77     Num add(Num x,Num y){
 78         Num ans;
 79         if (x.p==y.p){
 80             ans.p=x.p,ans.len=max(x.len,y.len);
 81             for(int i=0;i<ans.len;i++){
 82                 ans.a[i]+=x.a[i]+y.a[i];
 83                 if (ans.a[i]>=base){
 84                     ans.a[i]-=base;
 85                     ans.a[i+1]++;
 86                 }
 87             }
 88             if ((ans.len<5)&&(ans.a[ans.len]))ans.len++;
 89             return ans;
 90         }
 91         if (cmp(x,y)<0)swap(x,y);
 92         ans.p=x.p,ans.len=x.len;
 93         for(int i=0;i<ans.len;i++){
 94             ans.a[i]+=x.a[i]-y.a[i];
 95             if (ans.a[i]<0){
 96                 ans.a[i]+=base;
 97                 ans.a[i+1]--;
 98             }
 99         }
100         while ((ans.len)&&(!ans.a[ans.len-1]))ans.len--;
101         return ans;
102     }
103     Num dec(Num x,Num y){
104         y.p^=1;
105         return add(x,y);
106     }
107     Num mul(Num x,Num y){
108         Num ans;
109         ans.p=(x.p^y.p),ans.len=x.len+y.len-1;
110         for(int i=0;i<x.len;i++)
111             for(int j=0;j<y.len;j++){
112                 ans.a[i+j]+=x.a[i]*y.a[j];
113                 ans.a[i+j+1]+=ans.a[i+j]/base;
114                 ans.a[i+j]%=base;
115             }
116         if ((ans.len<5)&&(ans.a[ans.len]))ans.len++;
117         return ans;
118     }
119 };
120 namespace LCT{
121     int vis[M],st[M<<1],fa[M<<1],mn[M<<1],mx[M<<1],rev[M<<1],ch[M<<1][2];
122     int which(int k){
123         return ch[fa[k]][1]==k;
124     }
125     bool check(int k){
126         return ch[fa[k]][which(k)]==k;
127     }
128     void upd(int k){
129         rev[k]^=1;
130         swap(ch[k][0],ch[k][1]);
131     } 
132     void up(int k){
133         mn[k]=min(mn[ch[k][0]],mn[ch[k][1]]);
134         mx[k]=max(mx[ch[k][0]],mx[ch[k][1]]);
135         if (k>n){
136             mn[k]=min(mn[k],k-n);
137             mx[k]=max(mx[k],k-n); 
138         }
139     }
140     void down(int k){
141         if (rev[k]){
142             if (ch[k][0])upd(ch[k][0]);
143             if (ch[k][1])upd(ch[k][1]);
144             rev[k]=0;
145         }
146     }
147     void rotate(int k){
148         int f=fa[k],g=fa[f],p=which(k);
149         fa[k]=g;
150         if (check(f))ch[g][which(f)]=k;
151         fa[ch[k][p^1]]=f,ch[f][p]=ch[k][p^1];
152         fa[f]=k,ch[k][p^1]=f;
153         up(f),up(k);
154     }
155     void splay(int k){
156         for(int i=k;check(i);i=fa[i])st[++st[0]]=fa[i];
157         while (st[0])down(st[st[0]--]);
158         down(k);
159         for(int i=fa[k];check(k);i=fa[k]){
160             if (check(i)){
161                 if (which(i)==which(k))rotate(i);
162                 else rotate(k);
163             }
164             rotate(k);
165         }
166     }
167     void access(int k){
168         int lst=0;
169         while (k){
170             splay(k);
171             ch[k][1]=lst,up(k);
172             lst=k,k=fa[k];
173         }
174     }
175     void make_root(int k){
176         access(k);
177         splay(k);
178         upd(k);
179     }
180     int find_root(int k){
181         access(k);
182         splay(k);
183         while (ch[k][0]){
184             down(k);
185             k=ch[k][0];
186         }
187         splay(k);
188         return k;
189     }
190     void add(int x,int y){
191         make_root(x);
192         make_root(y);
193         fa[y]=x;
194     }
195     void del(int x,int y){
196         make_root(x);
197         access(y);
198         splay(x);
199         fa[y]=ch[x][1]=0,up(x);
200     }
201     int query_min(int x,int y){
202         make_root(x);
203         if (find_root(y)!=x)return 0;
204         return mn[x];
205     }
206     int query_max(int x,int y){
207         make_root(x);
208         if (find_root(y)!=x)return 0;
209         return mx[x];
210     }
211     int add_min(int id){
212         int pos=query_min(e[id].y,id+n);
213         if (pos){
214             vis[0]--,vis[pos]=0;
215             LCT::del(e[pos].y,pos+n);
216         }
217         vis[0]++,vis[id]=1;
218         LCT::add(e[id].y,id+n);
219         return pos;
220     }
221     int add_max(int id){
222         int pos=query_max(e[id].y,id+n);
223         if (pos){
224             vis[0]--,vis[pos]=0;
225             LCT::del(e[pos].y,pos+n);
226         }
227         vis[0]++,vis[id]=1;
228         LCT::add(e[id].y,id+n);
229         return pos;
230     }
231     void init(){
232         mn[0]=0x3f3f3f3f,mx[0]=0;
233         for(int i=1;i<=n+m;i++){
234             fa[i]=rev[i]=ch[i][0]=ch[i][1]=0;
235             up(i);
236         }
237         for(int i=1;i<=m;i++)add(e[i].x,i+n);
238     }
239     void clear(){
240         for(int i=1;i<=m;i++)
241             if (vis[i]){
242                 LCT::del(e[i].y,i+n);
243                 vis[0]--,vis[i]=0;
244             }
245     }
246 };
247 void get_LR(){
248     memset(L0,0,sizeof(L0));
249     memset(R0,0,sizeof(R0));
250     LCT::clear();
251     for(int i=1;i<=m;i++){
252         L0[i]=LCT::add_min(i);
253         if (L0[i])R0[L0[i]]=i;
254     }
255 }
256 void get_T12(){
257     LCT::clear();
258     for(int i=1;i<=m;i++){
259         if (vis0[i]){
260             for(int j=m;j;j--)
261                 if (LCT::vis[j])T1[i].push_back(j);
262         }
263         LCT::add_min(i);
264     }
265     LCT::clear();
266     for(int i=m;i;i--){
267         if (vis0[i]){
268             for(int j=1;j<=m;j++)
269                 if (LCT::vis[j])T2[i].push_back(j); 
270         }
271         LCT::add_max(i);
272     }
273 }
274 void get_v(){
275     v0.clear();
276     for(int i=1;i<=m;i++){
277         if (!L0[i])v0.push_back(make_pair(0,i));
278         else v0.push_back(make_pair((e[L0[i]].z+e[i].z<<1)+1,i));
279         if (R0[i])v0.push_back(make_pair((e[i].z+e[R0[i]].z<<1)+1,-i));
280     }
281 }
282 void upd_LR(int k){
283     LCT::clear();
284     memcpy(L,L0,sizeof(L));
285     L[k]=k;
286     for(int i=0;i<T2[k].size();i++){
287         L[T2[k][i]]=0;
288         LCT::add_max(T2[k][i]);
289     }
290     for(int i=0;i<T1[k].size();i++){
291         int pos=LCT::add_max(T1[k][i]);
292         if (pos)L[pos]=T1[k][i];
293     }
294     memset(R,0,sizeof(R));
295     for(int i=1;i<=m;i++)
296         if (L[i])R[L[i]]=i;
297 }
298 void upd_v(int k){
299     T1[k].push_back(k),T2[k].push_back(k);
300     for(int i=0;i<T1[k].size();i++){
301         int pos=T1[k][i];
302         if (R0[pos])v[k].push_back(make_pair((e[pos].z+e[R0[pos]].z<<1)+1,pos));
303         if (R[pos]){
304             v0.push_back(make_pair((e[pos].z+e[R[pos]].z<<1)+1,0));
305             v[k].push_back(make_pair((e[pos].z+e[R[pos]].z<<1)+1,-pos));
306         }
307     }
308     for(int i=0;i<T2[k].size();i++){
309         int pos=T2[k][i];
310         if (!L0[pos])v[k].push_back(make_pair(0,-pos));
311         else v[k].push_back(make_pair((e[L0[pos]].z+e[pos].z<<1)+1,-pos));
312         if (!L[pos]){
313             v0.push_back(make_pair(0,0));
314             v[k].push_back(make_pair(0,pos));
315         }
316         else{
317             v0.push_back(make_pair((e[L[pos]].z+e[pos].z<<1)+1,0));
318             v[k].push_back(make_pair((e[L[pos]].z+e[pos].z<<1)+1,pos));
319         }
320     }
321 }
322 void unique(){
323     sort(v0.begin(),v0.end());
324     q=0;
325     for(int i=0;i<v0.size();i++){
326         if ((!i)||(v0[i].fi!=v0[i-1].fi))Pos[++q]=v0[i].fi;
327         int pos=abs(v0[i].se);
328         if (v0[i].se>0)dS1[q]=Calc::add(dS1[q],z1[pos]),dS2[q]=Calc::add(dS2[q],z2[pos]);
329         else dS1[q]=Calc::dec(dS1[q],z1[pos]),dS2[q]=Calc::dec(dS2[q],z2[pos]);
330     }
331 }
332 void calc(){
333     sort(v0.begin(),v0.end());
334     S1=S2=0,ans.len=10;
335     for(int i=0;i<v0.size();i++){
336         int pos=abs(v0[i].se);
337         if (v0[i].se>0)S1=Calc::add(S1,z1[pos]),S2=Calc::add(S2,z2[pos]);
338         else S1=Calc::dec(S1,z1[pos]),S2=Calc::dec(S2,z2[pos]);
339         if ((i==v0.size())||(v0[i].fi!=v0[i+1].fi)){
340             if (!v0[i].fi)Check=S1;
341             ans=Calc::min(ans,Calc::dec(S2,Calc::mul(S1,S1)));
342         }
343     }
344     S1=S2=0;
345     memset(vis0,0,sizeof(vis0));
346     for(int i=0;i<v0.size();i++){
347         int pos=abs(v0[i].se);
348         vis0[pos]^=1;
349         if (v0[i].se>0)S1=Calc::add(S1,z1[pos]),S2=Calc::add(S2,z2[pos]);
350         else S1=Calc::dec(S1,z1[pos]),S2=Calc::dec(S2,z2[pos]);
351         if (((i==v0.size())||(v0[i].fi!=v0[i+1].fi))&&(!Calc::cmp(ans,Calc::dec(S2,Calc::mul(S1,S1)))))break;
352     }
353 }
354 void calc(int k){
355     for(int i=0;i<v[k].size();i++){
356         int q0=lower_bound(Pos+1,Pos+q+1,v[k][i].fi)-Pos,pos=abs(v[k][i].se);
357         if (v[k][i].se>0)dS1[q0]=Calc::add(dS1[q0],z1[pos]),dS2[q0]=Calc::add(dS2[q0],z2[pos]);
358         else dS1[q0]=Calc::dec(dS1[q0],z1[pos]),dS2[q0]=Calc::dec(dS2[q0],z2[pos]);
359     }
360     if (Calc::cmp(dS1[1],Check)<0){
361         ans=-1;
362         return;
363     } 
364     S1=S2=0,ans.len=10;
365     for(int i=1;i<=q;i++){
366         S1=Calc::add(S1,dS1[i]),S2=Calc::add(S2,dS2[i]);
367         ans=Calc::min(ans,Calc::dec(S2,Calc::mul(S1,S1)));
368     }
369     for(int i=0;i<v[k].size();i++){
370         int q0=lower_bound(Pos+1,Pos+q+1,v[k][i].fi)-Pos,pos=abs(v[k][i].se);
371         if (v[k][i].se<0)dS1[q0]=Calc::add(dS1[q0],z1[pos]),dS2[q0]=Calc::add(dS2[q0],z2[pos]);
372         else dS1[q0]=Calc::dec(dS1[q0],z1[pos]),dS2[q0]=Calc::dec(dS2[q0],z2[pos]);
373     }
374 }
375 int main(){
376     n=IO::read(),m=IO::read(),T=IO::read();
377     for(int i=1;i<=m;i++){
378         e[i].x=IO::read(),e[i].y=IO::read(),e[i].z=IO::read();
379         e[i].id=i;
380     }
381     sort(e+1,e+m+1);
382     LCT::init();
383     for(int i=1;i<=m;i++){
384         z1[i]=e[i].z;
385         z2[i]=Calc::mul(n-1,Calc::mul(z1[i],z1[i]));
386     }
387     get_LR();
388     if (LCT::vis[0]!=n-1){
389         if (T==1)IO::write(-1,'\n');
390         else{
391             for(int i=1;i<=m;i++)IO::write(-1,'\n');
392         }
393         return 0;
394     }
395     get_v(),calc();
396     if (T==1){
397         IO::write(ans,'\n');
398         return 0;
399     }
400     for(int i=1;i<=m;i++)
401         if (!vis0[i])Ans[e[i].id]=ans;
402     get_T12();
403     for(int i=1;i<=m;i++)
404         if (vis0[i])upd_LR(i),upd_v(i);
405     unique();
406     for(int i=1;i<=m;i++)
407         if (vis0[i]){
408             calc(i);
409             Ans[e[i].id]=ans;
410         }
411     for(int i=1;i<=m;i++)IO::write(Ans[i],'\n');
412     return 0;
413 }
View Code
复制代码

 

posted @   PYWBKTDA  阅读(485)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
历史上的今天:
2019-09-10 [bzoj5415]归程
点击右上角即可分享
微信分享提示