图的分块
基本思想是按点的度数划分为重点(deg>=sqrt(m))和轻点(deg<sqrt(m)), 轻点暴力更新, 重点只更新邻接的重点.
例1. hdu 4858 项目管理
该题算简单入门了.
#include <iostream> #include <algorithm> #include <math.h> #include <cstdio> #include <vector> #define pb push_back #define REP(i,a,n) for(int i=a;i<=n;++i) using namespace std; typedef long long ll; const int N = 1e5+10, INF = 0x3f3f3f3f; int n, m, q, sqn; int deg[N], u[N], v[N]; ll sum[N], val[N]; vector<int> g[N]; void work() { scanf("%d%d", &n, &m), sqn = sqrt(m); REP(i,1,n) g[i].clear(), deg[i]=sum[i]=val[i]=0; REP(i,1,m) { scanf("%d%d", u+i, v+i); ++deg[u[i]], ++deg[v[i]]; } REP(i,1,m) { if (deg[u[i]]>=sqn&°[v[i]]>=sqn) { g[u[i]].pb(v[i]),g[v[i]].pb(u[i]); } if (deg[u[i]]<sqn) g[u[i]].pb(v[i]); if (deg[v[i]]<sqn) g[v[i]].pb(u[i]); } scanf("%d", &q); REP(i,1,q) { int op, x, v; scanf("%d%d", &op, &x); if (op==0) { scanf("%d", &v); val[x] += v; for (int y:g[x]) if (deg[y]>=sqn) sum[y]+=v; } else { ll ans = 0; if (deg[x]<sqn) for (int y:g[x]) ans += val[y]; else ans = sum[x]; printf("%lld\n", ans); } } } int main() { int t; scanf("%d", &t); REP(i,1,t) work(); }
例2: hdu 4467 Graph
大意: 每个点都有一个颜色0或1, 每次询问求所有端点颜色为x,y的边权和, 每次修改翻转一个点的颜色.
思路还是同上题, 对于轻点的修改, 直接暴力. 对于重点的修改只暴力改重点之间的边, 然后再$O(1)$更新重点与轻点的边. 但本题比较卡常, 需要将边离散化去重一下.
其中$ans$用于记录答案, $val$用于记录每个重点与轻点相连边权和.
#include <iostream> #include <algorithm> #include <math.h> #include <cstdio> #include <set> #include <string> #include <vector> #define pb push_back #define REP(i,a,n) for(int i=a;i<=n;++i) using namespace std; typedef long long ll; const int N = 1e5+10, INF = 0x3f3f3f3f; int n, m, sqn, col[N]; struct _ { int u,v; ll w; bool operator < (const _ & rhs) const { if (u==rhs.u) return v<rhs.v; return u<rhs.u; } } e[N]; vector<_> g[N]; ll ans[3], val[N][2]; int deg[N]; void update(int x) { for (_ e:g[x]) { ans[col[x]+col[e.v]]-=e.w; ans[(col[x]^1)+col[e.v]]+=e.w; if (deg[x]<sqn) { val[e.v][col[x]]-=e.w; val[e.v][col[x]^1]+=e.w; } } if (deg[x]>=sqn) { ans[col[x]]-=val[x][0]; ans[col[x]+1]-=val[x][1]; ans[col[x]^1]+=val[x][0]; ans[(col[x]^1)+1]+=val[x][1]; } col[x] ^= 1; } void work() { REP(i,1,n) scanf("%d", col+i); REP(i,1,n) val[i][0]=val[i][1]=deg[i]=0, g[i].clear(); REP(i,0,2) ans[i]=0; REP(i,1,m) { scanf("%d%d%lld", &e[i].u, &e[i].v, &e[i].w); if (e[i].u>e[i].v) swap(e[i].u,e[i].v); } sort(e+1,e+1+m); int now = 0; REP(i,1,m) { if (e[i].u!=e[now].u||e[i].v!=e[now].v) e[++now]=e[i]; else e[now].w += e[i].w; } m = now, sqn = sqrt(m); REP(i,1,m) { ans[col[e[i].u]+col[e[i].v]] += e[i].w; ++deg[e[i].u],++deg[e[i].v]; } REP(i,1,m) { if (deg[e[i].u]>=sqn&°[e[i].v]>=sqn) { g[e[i].u].pb({0,e[i].v,e[i].w}); g[e[i].v].pb({0,e[i].u,e[i].w}); } if (deg[e[i].u]<sqn) g[e[i].u].pb({0,e[i].v,e[i].w}); if (deg[e[i].v]<sqn) g[e[i].v].pb({0,e[i].u,e[i].w}); } REP(x,1,n) if (deg[x]<sqn) { for (_ e:g[x]) { if (deg[e.v]>=sqn) val[e.v][col[x]]+=e.w; } } int q; scanf("%d", &q); REP(i,1,q) { char s[15]; int x, y; scanf("%s%d", s, &x); if (*s=='A') { scanf("%d", &y); printf("%lld\n", ans[x+y]); } else update(x); } } int main() { for (int kase = 1; ~scanf("%d%d", &n, &m); ++kase) { printf("Case %d:\n", kase); work(); } }