HDU 4858 分块

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4858

题意:中文题面

思路:来自此博客

对每个点定义两个值:val,sum,val记录自己的特征值,sum记录周边所有点特征值的和。

现在我们把所有的节点分成两类,重点(度数>=sqrt(m)),轻点(度数sqrt(m))。

插入:

轻点更新自己的val,同时更新所有的邻点的sum值

重点更新自己的val,同时只更新相邻重点的sum值(所以重点不需要连边到轻点)

查询:

轻点:暴力周边的所有邻点的val值。

重点:直接输出自己的sum值。

性质:

与重点相邻的重点不超过sqrt(m)个。

与轻点相邻的所有点不超过sqrt(m)个。

#define _CRT_SECURE_NO_DEPRECATE
#include<stdio.h>
#include<string.h>
#include<cstring>
#include<algorithm>
#include<queue>
#include<math.h>
#include<time.h>
#include<vector>
#include<iostream>
using namespace std;
typedef long long int LL;
const int MAXN = 100000 + 10;
struct Edges{
    int u, v; Edges(int u = 0, int v = 0) :u(u), v(v){};
};
vector<Edges>edge;
vector<int>G[MAXN];
int val[MAXN],du[MAXN];
LL sum[MAXN];
int main(){
//#ifdef kirito
//    freopen("in.txt", "r", stdin);
//    freopen("out.txt", "w", stdout);
//#endif
//    int start = clock();
    int t, n, m, q;
    scanf("%d", &t);
    while (t--){
        scanf("%d%d", &n, &m); edge.clear();  int block = (int)sqrt(m + 0.5);
        for (int i = 0; i <= n; i++){
            G[i].clear(); val[i] = 0; du[i] = 0; sum[i] = 0;
        }
        for (int i = 1; i <= m; i++){
            int u, v; scanf("%d%d", &u, &v);
            edge.push_back(Edges(u, v));
            du[u]++; du[v]++;
        }
        for (int i = 0; i < edge.size(); i++){
            int u = edge[i].u, v = edge[i].v;
            if (du[u] >= block&&du[v]>=block){
                G[u].push_back(v); G[v].push_back(u);
            }
            if (du[u] < block){
                G[u].push_back(v);
            }
            if (du[v] < block){
                G[v].push_back(u);
            }
        }
        scanf("%d", &q);
        while (q--){
            int type, pos, v; scanf("%d", &type);
            if (type){
                LL cnt = 0; scanf("%d", &pos);
                if (du[pos] < block){
                    for (int i = 0; i < G[pos].size(); i++){
                        cnt += val[G[pos][i]];
                    }
                }
                else{
                    cnt = sum[pos];
                }
                printf("%I64d\n", cnt);
            }
            else{
                scanf("%d %d", &pos, &v);
                val[pos] += v;
                for (int i = 0; i < G[pos].size(); i++){ sum[G[pos][i]] += v; }
            }
        }

    }
//#ifdef LOCAL_TIME
//    cout << "[Finished in " << clock() - start << " ms]" << endl;
//#endif
    return 0;
}

思路2:另一种方法。 可能由于数据弱,所以本题可以完全按照题意暴力。 不过暴力的复杂度是O(n^2)的。只是本题没卡该做法。

#define _CRT_SECURE_NO_DEPRECATE
#include<stdio.h>
#include<string.h>
#include<cstring>
#include<algorithm>
#include<queue>
#include<math.h>
#include<time.h>
#include<vector>
#include<iostream>
using namespace std;
typedef long long int LL;
const int MAXN = 100000 + 10;
vector<int>G[MAXN];
int val[MAXN];
int main(){
//#ifdef kirito
//    freopen("in.txt", "r", stdin);
//    freopen("out.txt", "w", stdout);
//#endif
//    int start = clock();
    int t, n, m, q;
    scanf("%d", &t);
    while (t--){
        scanf("%d%d", &n, &m);
        for (int i = 0; i <= n; i++){
            G[i].clear(); val[i] = 0;
        }
        for (int i = 1; i <= m; i++){
            int u, v; scanf("%d%d", &u, &v);
            G[u].push_back(v); G[v].push_back(u);
        }
        scanf("%d", &q);
        for (int i = 1; i <= q; i++){
            int type, pos, v;
            scanf("%d", &type);
            if (type){
                LL cnt = 0; scanf("%d", &pos);
                for (int k = 0; k < G[pos].size(); k++){
                    cnt += val[G[pos][k]];
                }
                printf("%I64d\n", cnt);
            }
            else{
                scanf("%d %d", &pos, &v);
                val[pos] += v;
            }
        }

    }
//#ifdef LOCAL_TIME
//    cout << "[Finished in " << clock() - start << " ms]" << endl;
//#endif
    return 0;
}
时间: 2024-08-10 00:04:44

HDU 4858 分块的相关文章

HDU 4467 分块

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4467 题意:给定n个点m条边的无向图,点被染色(黑0/白1),边带边权.然后q个询问.询问分为两种: Change u:把点u的颜色反转(黑变白,白变黑),Asksum a b(a,b的值为0/1):统计所以边的权值和,边的两个点满足一个点的颜色为a,一个点的颜色为b. 思路:考虑暴力的做法.修改可以做法O(1),但是查询就得O(m).所以总复杂度为O(m*q)会TLE.然后考虑图分块.参考HDU

hdu 4858 项目管理(vector模拟)

# include <stdio.h> # include <algorithm> # include <string.h> # include <vector> # define N 100005 using namespace std; vector<int>g[N]; int node[N]; int slove(int x) { int sum=0,i; for(i=0;i<g[x].size();i++) { sum+=node[

HDU 4858 项目管理(邻接表 暴力模拟)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4858 我们建造了一个大项目!这个项目有n个节点,用很多边连接起来,并且这个项目是连通的! 两个节点间可能有多条边,不过一条边的两端必然是不同的节点.每个节点都有一个能量值. 现在我们要编写一个项目管理软件,这个软件呢有两个操作:1.给某个项目的能量值加上一个特定值.2.询问跟一个项目相邻的项目的能量值之和.(如果有多条边就算多次,比如a和b有2条边,那么询问a的时候b的权值算2次). 解题报告:这个

HDU 5145 分块 莫队

给定n个数,q个询问[l,r]区间,每次询问该区间的全排列多少种. 数值都是30000规模 首先考虑计算全排列,由于有同种元素存在,相当于每次在len=r-l+1长度的空格随意放入某种元素即$\binom{len}{k_1}$,那么下种元素即为$\binom{len-k_1}{k2}$,以此类推,直至最后直接填满,那么全排列为${\frac{len!}{k_1!k_2!…k_n!}}$ 然后可以发现可以直接O(1)求得左右相邻区间的值(就是乘或除),那么考虑分块莫队. /** @Date : 2

HDU 5213 分块 容斥

给出n个数,给出m个询问,询问 区间[l,r] [u,v],在两个区间内分别取一个数,两个的和为k的对数数量. $k<=2*N$,$n <= 30000$ 发现可以容斥简化一个询问.一个询问的答案为 $[l,v]+(r,u)-[l,u)-(r,v]$,那么我们离线询问,将一个询问分成四个,分块暴力就行了. 然后就是注意细节,不要发生越界,访问错位置之类比较蠢的问题了. /** @Date : 2017-09-24 19:54:55 * @FileName: HDU 5213 分块 容斥.cpp

hdu 4858 项目管理 (图的分治)

项目管理 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 252    Accepted Submission(s): 88 Problem Description 我们建造了一个大项目!这个项目有n个节点,用很多边连接起来,并且这个项目是连通的! 两个节点间可能有多条边,不过一条边的两端必然是不同的节点. 每个节点都有一个能量值. 现

BestCoder Round #1 1002 项目管理 (HDU 4858)

项目管理 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 738    Accepted Submission(s): 260 Problem Description 我们建造了一个大项目!这个项目有n个节点,用很多边连接起来,并且这个项目是连通的!两个节点间可能有多条边,不过一条边的两端必然是不同的节点.每个节点都有一个能量值. 现在我

hdu 5193 分块 树状数组 逆序对

题意: 给出n个数,a1,a2,a3,...,an,给出m个修改,每个修改往数组的某个位置后面插入一个数,或者把某个位置上的数移除.求每次修改后逆序对的个数. 限制: 1 <= n,m <= 20000; 1 <= ai <= n 思路: 插入和删除用分块来处理,块与块之间用双向链表来维护,每一块用树状数组来求小于某个数的数有多少个. 外层可以使用分块维护下标,这样添加和删除元素的时候,也很方便,直接暴力.查找权值个数时,使用树状数组比较方便.内层通过树状数组维护权值. 每次更新即

hdu 4858 项目管理 图分治 (复合算法)

hdu 4858 项目管理 题意:给n(<=100000)个点,m条边( <=n+10),可能有重边,每个点有个值val,初识为0. 2种操作. 操作1:点x的值,加addx. 操作2:输出x点的邻点的val和. 分析:简单的优化操作1或操作2是不行的. 法一:针对点的度将图中点分为两类点.对于度大于sqrt (n)的点为重点,对于小于等于sqrt(n)的点为轻点. 重点的个数小于sqrt(n)个.针对重点和轻点分别处理. 法二:也可考虑每个点,将其邻点分类.大于该点度的点分为一类,等于该点的