JZOJ 5926. 【NOIP2018模拟10.25】naive 的图
Description
眾所周知,小 naive 有一張 n 個點,m 條邊的帶權無向圖。第 i 個點的顏色為 ci。d(s, t)表示從點 s 到點 t 的權值最小的路徑的權值,一條路徑的權值定義為路徑上權值最大的邊的權值。
求所有滿足 u < v, |cu ? cv| ≥ L 的點對 (u, v) 的 d(u, v) 之和。
Input
輸入文件為 graph.in。
第一行,三個整數 n, m, L,表示點數,邊數和參數 L。
第二行,n 個整數,第 i 個數為第 i 個點的顏色 ci。接下來 m 行,每行三個整數 ui, vi, wi,描述了一條邊。
Output
輸出文件為 graph.out。
共一行,一個整數,表示答案。
Sample Input
4 5 2
6 4 5 2
1 2 8
2 3 7
2 4 8
1 3 2
1 4 1
Sample Output
17
樣例解釋
滿足條件的點對:(1, 2),(1, 4),(2, 4),(3, 4),答案為 7 + 1 + 7 + 2 = 17。
Data Constraint
對于每個測試點內的數據:
Solution
-
首先我們發現兩點間的最短距離所經過的邊都在最小生成樹上。
-
于是我們建出Kruskal重構樹!
-
什么叫Kruskal重構樹呢?
-
我們在做Kruskal算法時不是每次選擇了一條邊作為最小生成樹上的一條邊嗎?
-
這是我們新建一個點分別連向那條邊的兩個端點,并將點權設為那條邊的邊權。
-
那么這個重構樹有什么性質呢?
-
于是當 L=0L=0L=0 時,一條邊(最小生成樹上)的貢獻次數就是其兩個子樹的 Size 取 min 。
-
那么我們遍歷這個重構樹,對于一個非葉子結點,枚舉其較小的子樹,
-
我們需要計算出每個點與其較大子樹中的點匹配所產生的答案。
-
容易想到離線后做二維數點,一維是dfs序(連續一段),
-
另一維是顏色c(兩段,c0≤c[x]?L或c0≥c[x]+Lc_0\leq c[x]-L\ 或\ c_0\geq c[x]+Lc0?≤c[x]?L?或?c0?≥c[x]+L)。
-
于是這樣排序后用樹狀數組計算即可。
-
時間復雜度 O(nlog2n)O(n\ log^2n)O(n?log2n) 。
-
這題還能用點分治做,我們考慮在最小生成樹上做點分治。
-
對于一個點為根的子樹,我們將其子樹內的點按邊權mx從小到大排序。
-
之后枚舉最大值是多少,那么前面的符合條件點就能用樹狀數組維護了。
-
時間復雜度同樣是 O(nlog2n)O(n\ log^2n)O(n?log2n) 。
Code
#include<cstdio> #include<cstring> #include<algorithm> #include<cctype> using namespace std; typedef long long LL; const int N=2e5+5; struct data {int x,y,z; }a[N*3],b[N*18],d[N*18]; int n,m,L,tot,cnt,num1,num2,pd; LL ans; int l[N<<1],r[N<<1],size[N<<1],dfn[N<<1]; int c[N],v[N<<1],f[N<<1]; inline int read() {int X=0,w=0; char ch=0;while(!isdigit(ch)) w|=ch=='-',ch=getchar();while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();return w?-X:X; } inline bool cmp(data x,data y) {return x.z<y.z; } inline bool cmpx(data x,data y) {return x.x<y.x || x.x==y.x && x.z<0 && y.z>=0; } inline bool cmpy(data x,data y) {return x.x>y.x || x.x==y.x && x.z<0 && y.z>=0; } int get(int x) {return f[x]==x?x:f[x]=get(f[x]); } void dg(int x,int y) {if(size[x]==1){if(c[x]-L>=0) b[++num1]=(data){c[x]-L,r[y],v[y]};d[++num2]=(data){c[x]+L+pd,r[y],v[y]};return;}dg(l[x],y);dg(r[x],y); } void dfs(int x) {size[x]=1;dfn[x]=++tot;if(!l[x]){b[++num1]=(data){c[x],dfn[x],-1};d[++num2]=(data){c[x],dfn[x],-1};return;}dfs(l[x]);size[x]+=size[l[x]];dfs(r[x]);size[x]+=size[r[x]];if(size[l[x]]>size[r[x]]) swap(l[x],r[x]);dg(l[x],x); } inline void change(int x) {while(x<=cnt) f[x]++,x+=x&-x; } inline int find(int x) {int sum=0;while(x) sum+=f[x],x-=x&-x;return sum; } int main() {freopen("graph.in","r",stdin);freopen("graph.out","w",stdout);n=cnt=read(),m=read(),L=read(),pd=!L?1:0;for(int i=1;i<=n;i++) c[f[i]=i]=read();for(int i=1;i<=m;i++) a[i].x=read(),a[i].y=read(),a[i].z=read();sort(a+1,a+1+m,cmp);for(int i=1,k=1;i<=m;i++){int x=get(a[i].x),y=get(a[i].y);if(x^y){f[++cnt]=cnt;f[x]=f[y]=cnt;l[cnt]=x;r[cnt]=y;v[cnt]=a[i].z;if(++k==n) break;}}tot=0;dfs(cnt);sort(b+1,b+1+num1,cmpx);memset(f,0,sizeof(f));for(int i=1;i<=num1;i++)if(b[i].z<0){change(b[i].y);}else{int num=find(dfn[b[i].y]+size[b[i].y]-1)-find(dfn[b[i].y]-1);ans+=(LL)num*b[i].z;}sort(d+1,d+1+num2,cmpy);memset(f,0,sizeof(f));for(int i=1;i<=num2;i++)if(d[i].z<0){change(d[i].y);}else{int num=find(dfn[d[i].y]+size[d[i].y]-1)-find(dfn[d[i].y]-1);ans+=(LL)num*d[i].z;}printf("%lld",ans);return 0; }總結
以上是生活随笔為你收集整理的JZOJ 5926. 【NOIP2018模拟10.25】naive 的图的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JZOJ 5924. 【NOIP2018
- 下一篇: JZOJ 5925. 【NOIP2018