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)级别. 确实很值得回味. 

     

View Code
#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;
}
原文地址:https://www.cnblogs.com/yefeng1627/p/3053653.html