hdu 4467 Graph 构造
写写思路吧,WA了数十次,都快哭了...改这改那的- -...A了以后都不知道先前哪里写搓了...
设定一个阀值sqrt(M),当顶点关联的边数量超过阀值,则令其为超级点,否则为普通点,
若边数量为M,则超级点数量不超过2*sqrt(M),
证明如下:
因为,无向图中,假定有2*sqrt(M)个顶点相关联的边至少为sqrt(M),则边数量为, 2*sqrt(M)*sqrt(M)/2 = M, 则意味着边数量大于M,所以矛盾, 得证.
我们设定一个数组 ans[3],来表示所求结果, 对于每个顶点添加一个sum[2],代表与其关联的顶点颜色为 0/1的边权和.
假定我们修改一个顶点x 的颜色, 若其颜色为 color, 与其相邻的顶点为0的边权和为sum[0], 顶点为1的边权和为1.
因为,我们只改变了顶点x的颜色,并未对边权做修改, 那么对最终ans[3]的影响, 只会导致:
ans[ color+0 ] - sum[0] && ans[color+1] - sum[1]
ans[ color^1+0 ] + sum[0] && sum[color^1+1] + sum[1]
这对我们来说是个好消息, 对于每次change操作, 若我们维护每个顶点的sum[0/1],即可维护最终结果数组ans[2][2], 我们再考虑,若change顶点x,
将会有何种影响.
首先对于节点x本身而言, 因为我们记录的sum[0/1],是指其相邻的节点颜色边权和,与x本身颜色无关,则意味着,我们不需要更新.
对于节点x相邻的节点y而言, y.sum[ x.color ] -= cost(x,y), y.sum[ x.color^1 ] += cost( x, y ).
那么基于此,我们就能够轻松维护题目所需答案了. 但是这里,因为我们每次需要维护其相邻的节点. 最极端情况有 M=10000条边.若10000次
询问都集中在这里, 那么时间复杂度会达到 10^8 , 5s肯定不够. 这里就用到了最上面的所设定的阀值了.
我们只对 超过阀值的 超级点维护 sum值, 因为, change操作一个顶点时, 不影响其本身顶点的sum值, 只会修改其相邻的信息.
但是若我们只用超级点的sum值来维护 ans,显然不行,还有那些普通点未计算.
大致思路是, 对于每个修改的顶点,首先修改其 相邻的超级点的sum[0/1]值. 因为超级点的数量不超过 2*sqrt(M),所以复杂度为2sqrt(M).
若 当前节点x是超级点, 则我们直接可以用其更新 ans数组, 若不是, 则我们需要对当前节点x,统计其所有相邻的sum[0/1]权值和, 然后用sum更新
ans数组,因为普通点相邻的顶点不超过sqrt(M)个, 则时间复杂度为 sqrt(M).
假定极端情况 10^5次change操作, 10^5条边. 时间复杂度为 O( Q*sqrt(M) ) . 接近于 10^(7.5) 左右. 解决本题还是足够了.
总结一下, 很少看到此类的构造, 接近于 O( N^(3/2) )的时间复杂度,来A题. 和上次的那道构造 O( N^(2/3) ) 有异曲同工之妙.
借助于一个阀值,将操作降低到 sqrt(N)级别. 确实很值得回味.
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #include<vector> using namespace std; typedef long long LL; const int N = 101010; int n, m; LL ans[3], sum[N][2]; vector<LL> c[N]; vector<int> lin[N]; int color[N], key[N], deg[N]; struct node{ int a,b; LL c; bool operator < (const node &tmp)const{ return (a<tmp.a)||(a==tmp.a&&b<tmp.b); } }e[N]; void init(){ int lim = (int)sqrt(n); for(int i = 0; i <= n; i++) c[i].clear(), lin[i].clear(); memset( sum, 0, sizeof(sum)); memset( ans, 0, sizeof(ans)); memset( key, 0, sizeof(key)); memset( deg, 0, sizeof(deg)); for(int i = 1; i <= n; i++) scanf("%d", &color[i] ); for(int i = 1; i <= m; i++){ scanf("%d%d%I64d",&e[i].a,&e[i].b,&e[i].c); if( e[i].a > e[i].b ) swap( e[i].a, e[i].b ); } sort( e+1, e+m+1 ); int t = 1; for(int i = 2; i <= m; i++) if( e[i].a != e[t].a || e[i].b != e[t].b ) e[++t] = e[i]; else e[t].c += e[i].c; m = t; for(int i = 1; i <= m; i++){ int a = e[i].a, b = e[i].b; ans[ color[a]+color[b] ] += e[i].c; deg[a]++; deg[b]++; } lim = round(sqrt(n)); for(int i = 1; i <= n; i++) if( deg[i] > lim ) key[i] = 1; for(int i = 1; i <= m; i++){ int a = e[i].a, b = e[i].b; sum[a][ color[b] ] += e[i].c; sum[b][ color[a] ] += e[i].c; if( key[a] && key[b] ){ lin[a].push_back(b); c[a].push_back(e[i].c); lin[b].push_back(a); c[b].push_back(e[i].c); } if( key[a] == 0 ) lin[a].push_back(b),c[a].push_back(e[i].c); if( key[b] == 0 ) lin[b].push_back(a),c[b].push_back(e[i].c); } } void change(int x){ if( key[x] ){ // 2*sqrt(M) for(int i = 0; i < (int)(lin[x].size()); i++){ sum[ lin[x][i] ][ color[x] ] -= c[x][i]; sum[ lin[x][i] ][ color[x]^1 ] += c[x][i]; } } else{ // sqrt(M) sum[x][0] = sum[x][1] = 0; for(int i = 0; i < (int)(lin[x].size()); i++){ sum[x][ color[ lin[x][i] ] ] += c[x][i]; if( key[ lin[x][i] ] ){ sum[ lin[x][i] ][ color[x] ] -= c[x][i]; sum[ lin[x][i] ][ color[x]^1 ] += c[x][i]; } } } ans[ color[x]+0 ] -= sum[x][0]; ans[ color[x]+1 ] -= sum[x][1]; ans[ (color[x]^1)+0 ] += sum[x][0]; ans[ (color[x]^1)+1 ] += sum[x][1]; color[x] ^= 1; } int main(){ int Case = 1; while( scanf("%d%d",&n,&m) != EOF){ printf("Case %d:\n", Case++); init(); char op[110]; int Q, a, b; scanf("%d", &Q ); while( Q-- ){ scanf("%s", op); if( op[0] == 'A' ){ scanf("%d%d",&a,&b); printf("%I64d\n", ans[a+b] ); } else{ scanf("%d",&a); change( a ); } } } return 0; }