Cdq分治整体二分学习记录
这点东西前前后后拖了好几个星期才学会……还是自己太菜啊。
Cdq分治的思想是:把问题序列分割成左右两个,先单独处理左边,再处理左边对右边的影响,再单独处理右边。这样可以消去数据结构上的一个log,降低编码复杂度。
整体二分:当一个询问的答案满足二分性质时,我们可以按照答案的大小分割整个查询和修改序列。每次把序列分成互不相同的两部分。这样能把数据结构的二分拿出来,降低编码复杂度。
说白了,就是当你懒得写树套树或者惨遭卡内存时候的骗分办法。
好了,上例题吧:
BZOJ2683:
二维单点加,矩形查。可以kdtree或树套树水过。
然而这里要用cdq分治。我们把一个查询利用二维前缀和原理拆成4个,然后按x排序,对t(时间)cdq分治,用树状数组维护y上的前缀和。我们先处理t在左边对右边查询的影响,然后再递归处理左右。
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define debug cout 6 using namespace std; 7 const int maxn=8e5+1e2; 8 9 int n; 10 11 struct BinaryIndexTree { 12 int dat[maxn]; 13 #define lowbit(x) (x&-x) 14 inline void update(int pos,int x) { 15 while( pos < maxn ) { 16 dat[pos] += x , 17 pos += lowbit(pos); 18 } 19 } 20 inline int query(int pos) { 21 int ret = 0; 22 while( pos ) 23 ret += dat[pos] , 24 pos -= lowbit(pos); 25 return ret; 26 } 27 }bit; 28 29 struct QNode { 30 int tpe,x,y,aid,tme,lam; 31 friend bool operator < (const QNode &a,const QNode &b) { 32 if( a.x != b.x ) 33 return a.x < b.x; 34 if( a.y != b.y ) 35 return a.y < b.y; 36 return a.tpe < b.tpe; 37 } 38 }ns[maxn],tp[maxn]; 39 40 int cnt,acn; 41 int ans[maxn]; 42 43 inline void cdq(int l,int r) { 44 if( l == r ) 45 return; 46 const int mid = ( l + r ) >> 1; 47 for(int i=l;i<=r;i++) { 48 if( ns[i].tme <= mid && ns[i].tpe == 1 ) 49 bit.update(ns[i].y,ns[i].lam); 50 if( ns[i].tme > mid && ns[i].tpe == 2 ) 51 ans[ns[i].aid] += ns[i].lam * bit.query(ns[i].y); 52 } 53 for(int i=l;i<=r;i++) 54 if( ns[i].tme <= mid && ns[i].tpe == 1 ) 55 bit.update(ns[i].y,-ns[i].lam); 56 int l1 = l , l2 = mid + 1; 57 for(int i=l;i<=r;i++) 58 if( ns[i].tme <= mid ) 59 tp[l1++] = ns[i]; 60 else 61 tp[l2++] = ns[i]; 62 for(int i=l;i<=r;i++) 63 ns[i] = tp[i]; 64 cdq(l,mid); 65 cdq(mid+1,r); 66 } 67 68 int main() { 69 static int t,x,y,xx,yy,lam; 70 scanf("%d",&n); 71 while( scanf("%d",&t) == 1 && t != 3 ) { 72 if( t == 1 ) { 73 scanf("%d%d%d",&x,&y,&lam); 74 ns[++cnt].tpe = t , ns[cnt].x = x , ns[cnt].y = y , 75 ns[cnt].lam = lam , ns[cnt].tme = cnt; 76 } 77 else { 78 scanf("%d%d%d%d",&x,&y,&xx,&yy); 79 --x , --y , ++acn; 80 ns[++cnt].tpe = t , ns[cnt].x = x , ns[cnt].y = y , 81 ns[cnt].aid = acn , ns[cnt].tme = cnt , ns[cnt].lam = 1; 82 ns[++cnt].tpe = t , ns[cnt].x = xx , ns[cnt].y = yy , 83 ns[cnt].aid = acn , ns[cnt].tme = cnt , ns[cnt].lam = 1; 84 ns[++cnt].tpe = t , ns[cnt].x = x , ns[cnt].y = yy , 85 ns[cnt].aid = acn , ns[cnt].tme = cnt , ns[cnt].lam = -1; 86 ns[++cnt].tpe = t , ns[cnt].x = xx , ns[cnt].y = y , 87 ns[cnt].aid = acn , ns[cnt].tme = cnt , ns[cnt].lam = -1; 88 } 89 } 90 sort(ns+1,ns+1+cnt); 91 cdq(1,cnt); 92 93 for(int i=1;i<=acn;i++) 94 printf("%d\n",ans[i]); 95 96 return 0; 97 }
BZOJ3110:
本来是一道树套树卡内存神题……
这题我们可以整体二分,对整个序列维护一个支持区间加,区间求和的树状数组(什么你不会?线段树也行),然后对于每个询问,如果>mid的区间和比查询的k小,则把询问分到左边,同时让k减去>mid的权值数量的区间和。反之直接分配到右边。
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define lli long long int 6 #define debug cout 7 using namespace std; 8 const int maxn=5e4+1e2; 9 10 struct BinaryIndexTree { 11 lli org[maxn],del[maxn]; 12 int siz; 13 #define lowbit(x) (x&-x) 14 15 inline void cupd(lli* dst,int pos,int add) { 16 while( pos <= siz ) 17 dst[pos] += add , pos += lowbit(pos); 18 } 19 inline lli cqry(lli* sou,int pos) { 20 lli ret = 0; 21 while( pos ) 22 ret += sou[pos] , pos -= lowbit(pos); 23 return ret; 24 } 25 inline lli qryp(int pos) { 26 return cqry(org,pos) + cqry(del,pos) * pos; 27 } 28 inline void update(int l,int r,int add) { 29 cupd(org,l,-(l-1)*add); 30 cupd(org,r,r*add); 31 cupd(del,l,add); 32 cupd(del,r,-add); 33 } 34 inline lli query(int l,int r) { 35 return qryp(r) - qryp(l-1); 36 } 37 inline void resize(int ss) { 38 siz = ss; 39 } 40 }bit; 41 42 int ans[maxn]; 43 44 struct QNode { // type == 1 means update , type == 2 means query; 45 int tpe,l,r,x,qid; 46 lli k; 47 }ns[maxn],tpl[maxn],tpr[maxn]; 48 49 inline void solve(int ql,int qr,int al,int ar) { 50 if( al == ar ) { 51 for(int i=ql;i<=qr;i++) 52 if( ns[i].tpe == 2 ) { 53 ans[ns[i].qid] = al; 54 } 55 return; 56 } 57 const int amid = ( al + ar ) >> 1; 58 int cntl = 0 , cntr = 0; 59 for(int i=ql;i<=qr;i++) { 60 if( ns[i].tpe == 1 ) { 61 if( ns[i].x <= amid ) { 62 tpl[++cntl] = ns[i]; 63 } else { 64 bit.update(ns[i].l,ns[i].r,1); 65 tpr[++cntr] = ns[i]; 66 } 67 } 68 else { 69 lli lam = bit.query(ns[i].l,ns[i].r); 70 if( ns[i].k <= lam ) { 71 tpr[++cntr] = ns[i]; 72 } else { 73 ns[i].k -= lam; 74 tpl[++cntl] = ns[i]; 75 } 76 } 77 } 78 for(int i=1;i<=cntr;i++) 79 if( tpr[i].tpe == 1 ) 80 bit.update(tpr[i].l,tpr[i].r,-1); 81 82 const int qmid = ql + cntl - 1; 83 int c1 = ql , c2 = qmid + 1; 84 for(int i=1;i<=cntl;i++) 85 ns[c1++] = tpl[i]; 86 for(int i=1;i<=cntr;i++) 87 ns[c2++] = tpr[i]; 88 89 solve(ql,qmid,al,amid); 90 solve(qmid+1,qr,amid+1,ar); 91 } 92 93 inline int getint() { 94 int ret = 0 , ch ; 95 while( !isdigit(ch=getchar()) ); 96 do ret=ret*10+ch-'0'; while( isdigit(ch=getchar()) ); 97 return ret; 98 } 99 signed main() { 100 static int n,m,qid; 101 n = getint() , m = getint(); 102 bit.resize(n); 103 for(int i=1;i<=m;i++) { 104 ns[i].tpe = getint() , ns[i].l = getint() , ns[i].r = getint(); 105 if( ns[i].tpe == 1 ) { 106 scanf("%d",&ns[i].x); 107 } else { 108 scanf("%lld",&ns[i].k); 109 ns[i].qid = ++qid; 110 } 111 } 112 113 solve(1,m,-n,n); 114 115 for(int i=1;i<=qid;i++) 116 printf("%d\n",ans[i]); 117 118 return 0; 119 }
BZOJ1901:
单点修改,查第k小的值。我们只需要把修改变成一个删除原权值的操作和一个插入新权值的操作,然后维护一个单点修改区间求和的树状数组就可以了。注意zoj上这题的双精题卡内存。
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cctype> 6 #define debug cout 7 using namespace std; 8 const int maxn=3e4+1e2; 9 10 struct BinaryIndexTree { 11 int dat[maxn],siz; 12 13 #define lowbit(x) (x&-x) 14 15 inline void update(int pos,int x) { 16 while( pos <= siz ) 17 dat[pos] += x , pos += lowbit(pos); 18 } 19 inline int query(int pos) { 20 int ret = 0; 21 while( pos ) 22 ret += dat[pos] , pos -= lowbit(pos); 23 return ret; 24 } 25 inline void resize(int ss) { 26 siz = ss; 27 } 28 }bit; 29 30 struct QNode { 31 int tpe; // tpe == 1 means query , 0 means change 32 int pos,val,del; 33 int st,ed,k,qid; 34 }ns[maxn],tp[2][maxn]; 35 36 int in[maxn],srt[maxn],ans[maxn],len; 37 38 inline void solve(int ql,int qr,int al,int ar) { 39 if( al == ar ) { 40 for(int i=ql;i<=qr;i++) 41 if( ns[i].tpe ) 42 ans[ns[i].qid] = al; 43 return; 44 } 45 const int amid = ( al + ar ) >> 1; 46 int cntl = 0 , cntr = 0; 47 for(int i=ql;i<=qr;i++) { 48 if( ns[i].tpe ) { 49 const int kk = bit.query(ns[i].ed) - bit.query(ns[i].st-1); 50 if( ns[i].k > kk ) { 51 ns[i].k -= kk , 52 tp[1][++cntr] = ns[i]; 53 } else tp[0][++cntl] = ns[i]; 54 } else { 55 if( ns[i].val <= amid ) { 56 bit.update(ns[i].pos,ns[i].del); 57 tp[0][++cntl] = ns[i]; 58 } else tp[1][++cntr] = ns[i]; 59 } 60 } 61 for(int i=ql;i<=qr;i++) 62 if( !ns[i].tpe && ns[i].val <= amid ) 63 bit.update(ns[i].pos,-ns[i].del); 64 const int qmid = ql + cntl - 1; 65 memcpy(ns+ql,tp[0]+1,sizeof(tp[0][0])*cntl); 66 memcpy(ns+qmid+1,tp[1]+1,sizeof(tp[1][0])*cntr); 67 solve(ql,qmid,al,amid); 68 solve(qmid+1,qr,amid+1,ar); 69 } 70 71 inline int getint() { 72 int ret = 0 , ch; 73 while( !isdigit(ch=getchar()) ); 74 do ret=ret*10+ch-'0'; while( isdigit(ch=getchar()) ); 75 return ret; 76 } 77 78 int main() { 79 static int n,m,cnt,qcnt; 80 static char com[10]; 81 n = getint() , m = getint(); 82 for(int i=1;i<=n;i++) { 83 ns[++cnt].val = in[i] = srt[++len] = getint() , 84 ns[cnt].pos = i;ns[cnt].del = 1; 85 } 86 for(int i=1,p;i<=m;i++) { 87 scanf("%s",com); 88 if( *com == 'Q' ) { 89 ns[++cnt].tpe = 1 , ns[cnt].qid = ++qcnt; 90 ns[cnt].st = getint() , ns[cnt].ed = getint() , ns[cnt].k = getint(); 91 } else { 92 p = getint(); 93 ns[++cnt].pos = p , ns[cnt].val = in[p] , ns[cnt].del = -1; 94 ns[++cnt].pos = p , srt[++len] = ns[cnt].val = in[p] = getint() , ns[cnt].del = 1; 95 } 96 } 97 sort(srt+1,srt+1+len) , len = unique(srt+1,srt+1+len) - srt - 1; 98 for(int i=1;i<=cnt;i++) 99 if( !ns[i].tpe ) { 100 ns[i].val = lower_bound(srt+1,srt+1+len,ns[i].val) - srt; 101 } 102 103 bit.resize(n); 104 solve(1,cnt,1,len); 105 106 for(int i=1;i<=qcnt;i++) 107 printf("%d\n",srt[ans[i]]); 108 109 return 0; 110 }
BZOJ1146:
这题可以权值线段树套树链剖分的线段树,也可以带修主席树。总之就是卡内存+不是人写的……
所以我们要整体二分,同上一题,只不过变到了树上。所以我们需要树链剖分线段树。
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cctype> 6 #define debug cerr 7 using namespace std; 8 const int maxn=8e4+1e2; 9 10 struct QNode { 11 int tpe; // 1 means query , 0 means change 12 int k,a,b,qid; 13 int pos,val,add; 14 }ns[maxn<<2],tp[2][maxn<<2]; 15 16 int in[maxn],srt[maxn<<1],ans[maxn],len; 17 int s[maxn],t[maxn<<1],nxt[maxn<<1]; 18 int fa[maxn],dep[maxn],siz[maxn],son[maxn],top[maxn],num[maxn],cov[maxn]; 19 int l[maxn<<3],r[maxn<<3],lson[maxn<<3],rson[maxn<<3],su[maxn<<3],cnt; 20 int n,m; 21 22 inline void build(int pos,int ll,int rr) { 23 l[pos] = ll , r[pos] = rr; 24 if( ll == rr ) return; 25 const int mid = ( ll + rr ) >> 1; 26 build(lson[pos]=++cnt,ll,mid); 27 build(rson[pos]=++cnt,mid+1,rr); 28 } 29 inline void update(int pos,int tar,int x) { 30 if( tar < l[pos] || r[pos] < tar ) return; 31 if( l[pos] == r[pos] && l[pos] == tar ) { 32 su[pos] += x; 33 return; 34 } 35 update(lson[pos],tar,x); 36 update(rson[pos],tar,x); 37 su[pos] = su[lson[pos]] + su[rson[pos]]; 38 } 39 inline int query(int pos,int ll,int rr) { 40 if( rr < l[pos] || r[pos] < ll ) return 0; 41 if( ll <= l[pos] && r[pos] <= rr ) return su[pos]; 42 return query(lson[pos],ll,rr) + query(rson[pos],ll,rr); 43 } 44 45 inline void addedge(int from,int to) { 46 static int cnt = 0; 47 t[++cnt] = to , nxt[cnt] = s[from] , s[from] = cnt; 48 } 49 inline void pre(int pos) { 50 siz[pos] = 1; 51 for(int at=s[pos];at;at=nxt[at]) 52 if( t[at] != fa[pos] ) { 53 fa[t[at]] = pos , dep[t[at]] = dep[pos] + 1; 54 pre(t[at]); 55 siz[pos] += siz[t[at]]; 56 son[pos] = siz[t[at]] > siz[son[pos]] ? t[at] : son[pos]; 57 } 58 } 59 inline void dfs(int pos) { 60 top[pos] = pos == son[fa[pos]] ? top[fa[pos]] : pos; 61 num[pos] = pos == son[fa[pos]] ? num[fa[pos]] + 1 : 1; 62 for(int at=s[pos];at;at=nxt[at]) 63 if( t[at] != fa[pos] ) 64 dfs(t[at]); 65 if( !son[pos] ) 66 build(cov[top[pos]]=++cnt,num[top[pos]],num[pos]); 67 } 68 inline int chain(int a,int b) { 69 int ret = 0; 70 while( top[a] != top[b] ) { 71 if( dep[top[a]] > dep[top[b]] ) { 72 ret += query(cov[top[a]],num[top[a]],num[a]), 73 a = fa[top[a]]; 74 } else { 75 ret += query(cov[top[b]],num[top[b]],num[b]), 76 b = fa[top[b]]; 77 } 78 } 79 ret += query(cov[top[a]],min(num[a],num[b]),max(num[a],num[b])); 80 return ret; 81 } 82 83 inline void solve(int ql,int qr,int al,int ar) { 84 if( al == ar ) { 85 for(int i=ql;i<=qr;i++) 86 if( ns[i].tpe ) 87 ans[ns[i].qid] = al; 88 return; 89 } 90 const int amid = ( al + ar ) >> 1; 91 int cntl = 0 , cntr = 0; 92 for(int i=ql;i<=qr;i++) { 93 if( ns[i].tpe ) { 94 int kk = chain(ns[i].a,ns[i].b); 95 if( ns[i].k > kk ) { 96 ns[i].k -= kk , tp[0][++cntl] = ns[i]; 97 } else tp[1][++cntr] = ns[i]; 98 } else { 99 if( ns[i].val > amid ) { 100 const int &pos = ns[i].pos; 101 update(cov[top[pos]],num[pos],ns[i].add); 102 tp[1][++cntr] = ns[i]; 103 } else tp[0][++cntl] = ns[i]; 104 } 105 } 106 for(int i=qr;i>=ql;i--) 107 if( ns[i].val > amid ) { 108 const int &pos = ns[i].pos; 109 update(cov[top[pos]],num[pos],-ns[i].add); 110 } 111 const int qmid = ql + cntl - 1; 112 113 memcpy( ns + ql , tp[0] + 1 , sizeof(tp[0][0]) * cntl ); 114 memcpy( ns + qmid + 1 , tp[1] + 1 , sizeof(tp[1][1]) * cntr ); 115 116 solve(ql,qmid,al,amid); 117 solve(qmid+1,qr,amid+1,ar); 118 } 119 120 inline int getint() { 121 int ret = 0 , ch; 122 while( !isdigit(ch=getchar()) ); 123 do ret=ret*10+ch-'0'; while( isdigit(ch=getchar()) ); 124 return ret; 125 } 126 int main() { 127 static int qcnt,qlen; 128 n = getint() , m = getint(); 129 qlen = n; 130 for(int i=1;i<=n;i++) 131 ns[i].pos = i , in[i] = ns[i].val = srt[++len] = getint() , ns[i].add = 1; 132 for(int i=1,a,b;i<n;i++) { 133 a = getint() , b = getint(); 134 addedge(a,b) , addedge(b,a); 135 } 136 for(int i=1,k,p;i<=m;i++) { 137 k = getint(); 138 if( !k ) { 139 p = getint(); 140 ++qlen , ns[qlen].pos = p , ns[qlen].val = in[p] , ns[qlen].add = -1; 141 ++qlen , ns[qlen].pos = p , in[p] = ns[qlen].val = srt[++len] = getint() , ns[qlen].add = 1; 142 } else { 143 ++qlen , ns[qlen].tpe = 1 , ns[qlen].a = getint() , ns[qlen].b = getint() , ns[qlen].k = k , ns[qlen].qid = ++qcnt; 144 } 145 } 146 147 sort(srt+1,srt+1+len); 148 len = unique(srt+1,srt+1+len) - srt - 1; 149 for(int i=1;i<=qlen;i++) 150 if( ! ns[i].tpe ) ns[i].val = lower_bound(srt+1,srt+1+len,ns[i].val) - srt; 151 152 pre(1);dfs(1); 153 solve(1,qlen,0,len); 154 155 for(int i=1;i<=qcnt;i++) 156 if( ans[i] ) printf("%d\n",srt[ans[i]]); 157 else puts("invalid request!"); 158 159 return 0; 160 }
BZOJ3237:
删除一组边问你是否联通。我们可以cdq分治求解。
维护每条边被删除的次数,递归左右时,如果这条边在某一侧被删除次数为0了则合并两边点所在的并查集。这里不要路径压缩,我们需要按秩合并。我们可以记录一个栈来回滚操作(当然你非得用主席树可持久化那也行)。查询时就看一下点1所在的联通块大小是否为n即可。是不是很好写啊?
代码:
1 #include<cstdio> 2 using namespace std; 3 const int maxn=1e5+1e2,maxm=2e5+1e2,maxk=1e5+1e2; 4 5 int fa[maxn],siz[maxn]; 6 int a[maxm],b[maxm]; 7 int len[maxk],met[maxk][6]; 8 int cnt[maxm],stk[maxn],top; 9 char ans[maxk]; 10 int n; 11 12 inline int findfa(int x) { 13 return fa[x] == x ? x : findfa(fa[x]); 14 } 15 16 inline void merge(int l,int r) { 17 for(int i=l;i<=r;i++) 18 for(int j=1;j<=len[i];j++) { 19 const int k = met[i][j]; 20 if( --cnt[k] ) continue; 21 int aa = findfa(a[k]) , bb = findfa(b[k]); 22 if( aa == bb ) continue; 23 if( siz[aa] > siz[bb] ) fa[bb] = aa , siz[aa] += siz[bb] , stk[++top] = bb; 24 else fa[aa] = bb , siz[bb] += siz[aa] , stk[++top] = aa; 25 } 26 } 27 inline void resetcnt(int l,int r) { 28 for(int i=l;i<=r;i++) 29 for(int j=1;j<=len[i];j++) 30 ++cnt[met[i][j]]; 31 } 32 inline void resetfa(int tar) { 33 while( top > tar ) { 34 const int pos = stk[top--]; 35 siz[fa[pos]] -= siz[pos] , fa[pos] = pos; 36 } 37 } 38 39 inline void solve(int ll,int rr) { 40 if( ll == rr ) { 41 ans[ll] = ( siz[findfa(1)] == n ); 42 return; 43 } 44 const int mid = ( ll + rr ) >> 1 , mem = top; 45 46 merge(ll,mid); 47 solve(mid+1,rr); 48 resetfa(mem); 49 resetcnt(ll,mid); 50 51 merge(mid+1,rr); 52 solve(ll,mid); 53 resetfa(mem); 54 resetcnt(mid+1,rr); 55 } 56 57 inline void init() { 58 for(int i=1;i<=n;i++) 59 fa[i] = i , siz[i] = 1; 60 } 61 62 int main() { 63 static int m,k; 64 scanf("%d%d",&n,&m); 65 for(int i=1;i<=m;i++) 66 scanf("%d%d",a+i,b+i); 67 scanf("%d",&k); 68 for(int i=1;i<=k;i++) { 69 scanf("%d",len+i); 70 for(int j=1;j<=len[i];j++) 71 scanf("%d",met[i]+j); 72 } 73 74 init(); 75 resetcnt(1,k); 76 for(int i=1;i<=m;i++) 77 if( !cnt[i] ) { 78 int aa = findfa(a[i]) , bb = findfa(b[i]); 79 if( aa == bb ) continue; 80 if( siz[aa] > siz[bb] ) fa[bb] = aa , siz[aa] += siz[bb]; 81 else fa[aa] = bb , siz[bb] += siz[aa]; 82 } 83 84 solve(1,k); 85 86 for(int i=1;i<=k;i++) 87 puts( ans[i] ? "Connected" : "Disconnected" ); 88 89 return 0; 90 }
BZOJ2001:
动态维护最小生成树。
我们可以LCT,可做但据说并不能AC(常数巨大)。
所以我们需要cdq分治。对于每层,我们删除无用边,缩点必需边,可以把边数化成O(n)级别的,然后再向下递归即可。底层直接更新权值,计算最小生成树,记录答案。
实现的时候,我们记录每一条边实际权值,和它原来的编号和在当前边序列中的编号的映射关系即可。每层并查集初始化的时候仅初始化在这层存在的边。
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cctype> 5 #define lli long long int 6 using namespace std; 7 const int maxn=2e4+1e2,maxm=5e4+1e2,maxd=25; 8 const int inf=0x3f3f3f3f; 9 10 struct Edge { 11 int a,b,val,id; 12 friend bool operator < (const Edge &a,const Edge &b) { 13 return a.val < b.val; 14 } 15 }ess[maxd][maxm],now[maxm],etp[maxm]; 16 int opeid[maxm],opeval[maxm]; 17 int wei[maxm],cov[maxm]; // weight of edge , convert real id to operated id. 18 int fa[maxn]; 19 lli ans[maxm]; 20 21 inline int findfa(int x) { 22 return fa[x] == x ? x : fa[x] = findfa(fa[x]); 23 } 24 inline void merge(int x,int y) { 25 x = findfa(x) , y = findfa(y); 26 if( x == y ) return; 27 fa[x] = y; 28 } 29 inline void reset(Edge* es,int len) { 30 for(int i=1;i<=len;i++) 31 fa[es[i].a] = es[i].a , 32 fa[es[i].b] = es[i].b; 33 } 34 35 inline lli contraction(Edge* es,int &len) { 36 lli ret = 0; 37 int tpl = 0; 38 39 reset(es,len); 40 sort(es+1,es+1+len); 41 for(int i=1;i<=len;i++) 42 if( findfa(es[i].a) != findfa(es[i].b) ) { // find a mst 43 etp[++tpl] = es[i] , merge(es[i].a,es[i].b); 44 } 45 46 reset(etp,tpl); 47 for(int i=1;i<=tpl;i++) 48 if( etp[i].val != -inf ) { 49 ret += etp[i].val , merge(etp[i].a,etp[i].b); 50 } 51 52 tpl = 0; 53 for(int i=1;i<=len;i++) 54 if( findfa(es[i].a) != findfa(es[i].b) ) { 55 etp[++tpl] = es[i]; 56 etp[tpl].a = findfa(es[i].a) , etp[tpl].b = findfa(es[i].b) , 57 cov[es[i].id] = tpl; 58 } 59 60 reset(es,len); 61 len = tpl; 62 memcpy(es+1,etp+1,sizeof(es[0])*len); 63 64 return ret; 65 } 66 inline void reduction(Edge* es,int &len) { 67 int tpl = 0; 68 69 reset(es,len); 70 sort(es+1,es+1+len); 71 for(int i=1;i<=len;i++) 72 if( findfa(es[i].a) != findfa(es[i].b) ) { 73 merge(es[i].a,es[i].b); 74 etp[++tpl] = es[i]; 75 cov[es[i].id] = tpl; 76 } 77 else if( es[i].val == inf ) { 78 etp[++tpl] = es[i]; 79 cov[es[i].id] = tpl; 80 } 81 82 reset(es,len) , len = tpl; 83 memcpy(es+1,etp+1,sizeof(es[0])*len); 84 } 85 inline lli mst(Edge* es,int len) { 86 lli ret = 0; 87 reset(es,len); 88 sort(es+1,es+1+len); 89 for(int i=1;i<=len;i++) 90 if( findfa(es[i].a) != findfa(es[i].b) ) 91 ret += es[i].val , merge(es[i].a,es[i].b); 92 return ret; 93 } 94 95 inline void solve(Edge* es,int dep,int len,int ll,int rr,lli res) { 96 if( ll == rr ) 97 wei[opeid[ll]] = opeval[ll]; 98 for(int i=1;i<=len;i++) { 99 es[i].val = wei[es[i].id]; 100 now[i] = es[i] , cov[now[i].id] = i; 101 } 102 if( ll == rr ) { 103 ans[ll] = res + mst(now,len); 104 return; 105 } 106 107 108 for(int i=ll;i<=rr;i++) { 109 now[cov[opeid[i]]].val = -inf; 110 } 111 res += contraction(now,len); 112 113 114 for(int i=ll;i<=rr;i++) 115 now[cov[opeid[i]]].val = inf; 116 reduction(now,len); 117 118 memcpy(ess[dep+1]+1,now+1,sizeof(now[0])*len); 119 120 const int mid = ( ll + rr ) >> 1; 121 solve(ess[dep+1],dep+1,len,ll,mid,res); 122 solve(ess[dep+1],dep+1,len,mid+1,rr,res); 123 } 124 125 inline char nextchar() { 126 static char buf[1<<22],*st=buf+(1<<22),*ed=buf+(1<<22); 127 if( st == ed ) ed = buf + fread(st=buf,1,1<<22,stdin); 128 return st != ed ? *st++ : -1; 129 } 130 inline int getint() { 131 int ret = 0,ch; 132 while( !isdigit(ch=nextchar()) ); 133 do ret=ret*10+ch-'0'; while( isdigit(ch=nextchar()) ); 134 return ret; 135 } 136 int main() { 137 static int n,m,q; 138 scanf("%d%d%d",&n,&m,&q); 139 for(int i=1,a,b,l;i<=m;i++) { 140 scanf("%d%d%d",&a,&b,&l); 141 ess[0][i] = (Edge){a,b,l,i}; 142 wei[i] = l; 143 } 144 for(int i=1;i<=q;i++) 145 scanf("%d%d",opeid+i,opeval+i); 146 147 solve(ess[0],0,m,1,q,0); 148 149 for(int i=1;i<=q;i++) 150 printf("%lld\n",ans[i]); 151 152 return 0; 153 }
为什么代码都写得这么长这么丑还常数那么大?……果然还是自己太菜啊。