有一個 \(n\times n\) 的矩陣 \(A\)。最開始 \(A\) 中每一個元素的值都爲 \(0\)。算法
有 \(m\) 次操做,每次給你 \(x_1,x_2,y_1,y_2,w\),對於知足 \(x_1\leq i\leq x_2,y_1\leq j\leq y_2\) 的數對 \((i,j)\),把 \(A_{i,j}\) 的值增長 \(w\)。dom
最後構造一個 \(n\) 個點的無向圖 \(G\)。對於知足 \(1\leq i<j\leq n\) 的數對 \((i,j)\),在 \(G\) 中加一條鏈接着 \(i,j\),邊權爲 \(A_{i,j}\) 的邊。ui
求 \(G\) 的最小生成樹的邊權和。spa
\(1\leq n,m\leq 100000,1\leq x_1\leq x_2<y_1\leq y_2\leq n,-{10}^6\leq w\leq {10}^6\);code
彷佛 prim 和 kruskal 算法都不太好作這道題。get
還有個算法叫 boruvka。string
大概就是每一輪對於每一個連通塊求出這個連通塊與其餘連通塊間邊權最小的邊,而後把這兩個連通塊縮在一塊兒。it
總共會縮 \(O(\log n)\) 輪。io
用掃描線+線段樹處理出最小值和(連通塊與最小值不一樣)的最小值。function
這樣若是最小值和 \(i\) 在同一個連通塊中,就選第二個就行了。
時間複雜度:\(O(m\log^2n)\)
#include<cstdio> #include<cstring> #include<algorithm> #include<cstdlib> #include<ctime> #include<functional> #include<cmath> #include<vector> #include<assert.h> //using namespace std; using std::min; using std::max; using std::swap; using std::sort; using std::reverse; using std::random_shuffle; using std::lower_bound; using std::upper_bound; using std::unique; using std::vector; typedef long long ll; typedef unsigned long long ull; typedef double db; typedef std::pair<int,int> pii; typedef std::pair<ll,ll> pll; void open(const char *s){ #ifndef ONLINE_JUDGE char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout); #endif } void open2(const char *s){ #ifdef DEBUG char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout); #endif } int rd(){int s=0,c,b=0;while(((c=getchar())<'0'||c>'9')&&c!='-');if(c=='-'){c=getchar();b=1;}do{s=s*10+c-'0';}while((c=getchar())>='0'&&c<='9');return b?-s:s;} void put(int x){if(!x){putchar('0');return;}static int c[20];int t=0;while(x){c[++t]=x%10;x/=10;}while(t)putchar(c[t--]+'0');} int upmin(int &a,int b){if(b<a){a=b;return 1;}return 0;} int upmax(int &a,int b){if(b>a){a=b;return 1;}return 0;} const ll inf=0x3fffffffffffffffll; typedef std::pair<pll,pll> orzzjt; const int N=100010; orzzjt merge(orzzjt a,orzzjt b) { orzzjt c; if(a.first<b.first) { c=a; if(b.first.second!=c.first.second) c.second=min(c.second,b.first); else c.second=min(c.second,b.second); } else { c=b; if(a.first.second!=c.first.second) c.second=min(c.second,a.first); else c.second=min(c.second,a.second); } return c; } int c[N]; namespace seg { orzzjt s[4*N]; ll t[4*N]; #define mid ((L+R)>>1) #define lc (p<<1) #define rc ((p<<1)|1) void mt(int p) { s[p]=merge(s[lc],s[rc]); } void build(int p,int L,int R) { t[p]=0; if(L==R) { s[p].first=pll(0,c[L]); s[p].second=pll(inf,0); return; } build(lc,L,mid); build(rc,mid+1,R); mt(p); } void add(int p,ll v) { t[p]+=v; s[p].first.first+=v; s[p].second.first+=v; } void push(int p) { if(t[p]) { add(lc,t[p]); add(rc,t[p]); t[p]=0; } } void add(int p,int l,int r,ll v,int L,int R) { if(l<=L&&r>=R) { add(p,v); return; } push(p); if(l<=mid) add(lc,l,r,v,L,mid); if(r>mid) add(rc,l,r,v,mid+1,R); mt(p); } orzzjt query(int p,int l,int r,int L,int R) { if(l<=L&&r>=R) return s[p]; push(p); if(r<=mid) return query(lc,l,r,L,mid); if(l>mid) return query(rc,l,r,mid+1,R); return merge(query(lc,l,r,L,mid),query(rc,l,r,mid+1,R)); } } struct info { int x,y1,y2,w; info(int a=0,int b=0,int c=0,int d=0):x(a),y1(b),y2(c),w(d){} }; int cmp(info a,info b) { return a.x<b.x; } info a[4*N]; int n,m; int t; int cnt; int f[N]; ll ans; int find(int x) { return f[x]==x?x:f[x]=find(f[x]); } int merge(int x,int y) { if(find(x)==find(y)) return 0; f[find(x)]=find(y); return 1; } pll h[N]; int e[N]; int main() { open("72G"); scanf("%d%d",&n,&m); int x1,x2,y1,y2,w; for(int i=1;i<=m;i++) { scanf("%d%d%d%d%d",&x1,&x2,&y1,&y2,&w); a[++t]=info(x1,y1,y2,w); a[++t]=info(x2+1,y1,y2,-w); a[++t]=info(y1,x1,x2,w); a[++t]=info(y2+1,x1,x2,-w); } sort(a+1,a+t+1,cmp); cnt=n; for(int i=1;i<=n;i++) { f[i]=i; c[i]=i; e[i]=i; } while(cnt>1) { seg::build(1,1,n); int j=1; for(int i=1;i<=cnt;i++) h[i]=pll(inf,inf); for(int i=1;i<=n;i++) { for(;j<=t&&a[j].x==i;j++) seg::add(1,a[j].y1,a[j].y2,a[j].w,1,n); orzzjt s; if(i==1) s=seg::query(1,i+1,n,1,n); else if(i==n) s=seg::query(1,1,i-1,1,n); else s=merge(seg::query(1,1,i-1,1,n),seg::query(1,i+1,n,1,n)); if(s.first.second!=c[i]) h[c[i]]=min(h[c[i]],s.first); else h[c[i]]=min(h[c[i]],s.second); } for(;j<=t;j++) seg::add(1,a[j].y1,a[j].y2,a[j].w,1,n); for(int i=1;i<=cnt;i++) if(merge(e[i],e[h[i].second])) ans+=h[i].first; cnt=0; for(int i=1;i<=n;i++) if(find(i)==i) { c[i]=++cnt; e[cnt]=i; } for(int i=1;i<=n;i++) c[i]=c[find(i)]; } printf("%lld\n",ans); return 0; }