NOIP模擬 graph(啟發式合併)
阿新 • • 發佈:2018-10-31
額呵呵呵
【題目分析】
我選擇死亡。。。。。。。。我的天哪ldx大佬竟然吊打了標程?究竟是。。還是。。
蒟蒻只能暴力列舉拿著30分滾粗。。。點分治是什麼?完全沒想到好嗎。。。。。
首先最後能做出貢獻的邊一定是在最小生成樹上,這個應該不需要證吧。。。。。
然後考慮在加邊的過程中,連線兩個聯通塊能產生的貢獻就是兩個聯通塊內滿足限制的點對數*邊權,所以計算還是很容易的。
然後考慮如何去尋找一個點與其滿足限制的點對。這裡選擇主席樹,每次只要查詢比col[i]-L小的與col[i]+L大的即可。
求完貢獻後考慮合併兩個聯通塊的資訊,直接啟發式合併即可,將size小的直接往size大的聯通塊加就行了。
(一點小建議:不要隨便全域性long long!容易TLE!)
【程式碼~】
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int MAXN=2e5+10; const int MAXM=1e6+10; int n,m,cnt,tot,k,maxx; int col[MAXN],siz[MAXN],fa[MAXN],rt[MAXN]; struct Edge{ int from,to,w; friend inline bool operator<(const Edge &a,const Edge &b){ return a.w<b.w; } }graph[MAXM],edge[MAXM]; struct Tree{ int l,r; int sum; }tr[MAXN*50]; struct node{ int fa,color; friend inline bool operator<(const node &a,const node &b){ if(a.fa==b.fa) return a.color<b.color; return a.fa<b.fa; } }; vector<node> vec[MAXN]; inline int Read() { int i=0,f=1; char c; for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar()); if(c=='-') f=-1,c=getchar(); for(;c>='0'&&c<='9';c=getchar()) i=(i<<3)+(i<<1)+c-'0'; return i*f; } void insert(int &root,int l,int r,int x,int key) { if(!root) root=++tot; tr[root].sum+=key; if(l==r) return ; int mid=l+r>>1; if(x<=mid) insert(tr[root].l,l,mid,x,key); else insert(tr[root].r,mid+1,r,x,key); } int merge(int x,int y,int l,int r) { if(!x||!y) return x+y; int mid=l+r>>1; tr[x].l=merge(tr[x].l,tr[y].l,l,mid); tr[x].r=merge(tr[x].r,tr[y].r,mid+1,r); tr[x].sum+=tr[y].sum; return x; } int query(int root,int l,int r,int L,int R) { if(l>R||r<L) return 0; if(L<=l&&r<=R) return tr[root].sum; int mid=l+r>>1; if(R<=mid) return query(tr[root].l,l,mid,L,R); else { if(L>mid) return query(tr[root].r,mid+1,r,L,R); else return query(tr[root].l,l,mid,L,mid)+query(tr[root].r,mid+1,r,mid+1,R); } } inline int solve(int root,int x,int k) { int s=0,t=maxx; return query(root,0,maxx,max(s,x-k+1),min(x+k-1,t)); } inline int find(int x) { if(x==fa[x]) return x; return fa[x]=find(fa[x]); } inline void kruskal() { for(int i=1;i<=n;++i) fa[i]=i; sort(graph+1,graph+m+1); int sum=0; for(int i=1;i<=m;++i) { int u=graph[i].from,v=graph[i].to; u=find(u),v=find(v); if(u==v) continue; sum++; fa[v]=u; edge[++cnt]=graph[i]; if(sum==n-1) break; } } inline void hebing(int x,int y) { int siz1=vec[x].size(); for(int i=0;i<siz1;++i) vec[y].push_back(vec[x][i]); rt[y]=merge(rt[y],rt[x],0,maxx); fa[x]=y; siz[y]+=siz[x]; } int main() { n=Read(),m=Read(),k=Read(); for(int i=1;i<=n;++i) col[i]=Read(),maxx=max(maxx,col[i]); for(int i=1;i<=m;++i) graph[i].from=Read(),graph[i].to=Read(),graph[i].w=Read(); kruskal(); tot=n; for(int i=1;i<=n;++i) { fa[i]=i,siz[i]=1; node x; x.fa=i,x.color=col[i]; vec[i].push_back(x); rt[i]=i; insert(rt[i],0,maxx,col[i],1); } LL ans=0; for(int i=1;i<=cnt;++i) { int u=edge[i].from,v=edge[i].to; int fu=find(u),fv=find(v); if(siz[fu]>siz[fv]) swap(fu,fv),swap(u,v); int siz1=vec[fu].size(),siz2=vec[fv].size(); for(int j=0;j<siz1;++j) { LL tmp=(siz2-solve(rt[fv],vec[fu][j].color,k)); ans+=edge[i].w*tmp; } hebing(fu,fv); } cout<<ans; return 0; }