zoukankan      html  css  js  c++  java
  • [计蒜客 A1998]Ka Chang

    题意:给一棵根为1号结点的树,每个点有权值,初始是0,(Q) 次操作,每次:

    1. 对深度为 (L) 的点全部加权值 (x)(根结点深度为0)
    2. 输出子树权值和

    (n,Q leq 10^5)

    先考虑操作2,显然这是个树上子树统计问题,dfs序+线段树/树状数组就可以在(O(log n))复杂度内完成。但是操作1,如果直接修改复杂度最坏则是(O(nlogn))(菊花图),于是我们想到了一个最为经典的降低时间复杂度的方法——均摊。

    思想就是因为操作2复杂度低,操作1复杂度高,那么就通过均摊一下把整体复杂度降低。

    实现方法类似于分块。首先我们统计出每一层有哪些结点,然后根据结点个数分类。不妨规定一个(m),当一层结点个数 (<m) 时进行暴力修改,复杂度(O(mlog n)),当结点个数(>m) 时打个标记。显然打标记的层数应该是不超过(frac{n}{m})个。然后统计答案时,对于每一个打标记的层,计算一下哪些结点在所求点的子树内。由于我们是按照dfs序统计的,那么每一层内dfs序是单调递增的,于是可以二分,对于统计的结点(x),找到dfs序位于([dfn[x],dfn[x]+siz[x]])的结点,统计答案即可。

    时间复杂度为(O(qmlog n + qfrac{n}{m}log n)),当 (m)(sqrt{n})时复杂度最小,为(O(qsqrt{n} log n))

    
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<cstdlib>
    #include<vector>
    #include<set>
    #include<map>
    #include<string>
    #include<iostream>
    #include<queue>
    #include<cctype>
    #include<ctime>
    using namespace std;
    
    #define A(x) cout << #x << " " << x << endl;
    #define AA(x,y) cout << #x << " " << x << " " << #y << " " << y << endl;
    #define AAA(x,y,z) cout << #x << " " << x << " " << #y << " " << y  << " " << #z << " " << z << endl;
    #define B cout << "Break" << endl;
    #define ll long long
    #define inf 1000000000
    
    int read()
    {
    	char c = getchar();
    	int x = 0,f = 1;
    	while(!isdigit(c))
    	{
    		if(c == '-') f = -1;
    		c = getchar();
    	}
    	while(isdigit(c))
    	{
    		x = x * 10 + c - '0';
    		c = getchar();
    	}
    	return f * x;
    }
    #define N 100010
    int head[N],nxt[N << 1],to[N << 1],dfn[N],til[N];//存树
    ll tag[N],c[N];
    vector<int>d[N];
    vector<int>dep_geq_sqrt;
    int n,q,ecnt,maxdep,tim;
    void add_edge(int u,int v)
    {
    	nxt[++ecnt] = head[u];
    	head[u] = ecnt;
    	to[ecnt] = v;
    	return;
    }
    ll lowbit(ll x) {return x & -x;}
    void add(ll x,ll k)
    {
    	while(x <= n)
    	{
    		c[x] += k;
    		x += lowbit(x);
    	}
    	return;
    }
    ll query(ll x)
    {
    	ll ret = 0;
    	while(x > 0)
    	{
    		ret += c[x];
    		x -= lowbit(x);
    	}
    	return ret;
    }
    void dfs(int u,int fa,int dep)
    {
    	maxdep = max(maxdep,dep);
    	dfn[u] = ++tim;
    	d[dep].push_back(dfn[u]);
    	for(int i = head[u];i;i = nxt[i])
    	{
    		int v = to[i];
    		if(v == fa) continue;
    		dfs(v,u,dep + 1);
    	}
    	til[u] = tim;
    	return;
    }
    int main()
    {
    	n = read(),q = read();
    	for(int i = 1;i < n;++i)
    	{
    		int u = read(),v = read();
    		add_edge(u,v);add_edge(v,u);
    	}
    	dfs(1,0,0);
    	int m = sqrt(n);
    	for(int i = 0;i <= maxdep;++i)
    		if(d[i].size() > m) dep_geq_sqrt.push_back(i);
    	while(q--)
    	{
    		int op = read();
    		if(op == 1)
    		{
    			int l = read(),x = read();
    			if(d[l].size() > m)
    				tag[l] += x;
    			else
    				for(auto u : d[l]) add(u,x);
    		}
    		else
    		{
    			int x = read();
    			ll ans = query(til[x]) - query(dfn[x] - 1);
    			for(auto dep : dep_geq_sqrt)
    			{
    				auto l = lower_bound(d[dep].begin(),d[dep].end(),dfn[x]);
    				auto r = upper_bound(d[dep].begin(),d[dep].end(),til[x]);
    				ans += 1ll * tag[dep] * (r - l);
    			}
    			printf("%lld
    ",ans);
    		}
    	}
    }
    
  • 相关阅读:
    PHP学习—了解篇2
    PHP学习—了解篇
    德国山寨史
    Network client/server
    Linux (二) vi
    I2C 总线协议
    Verilog (二) multiplexer and decoder
    有限状态机HDL模板
    "德意志之歌"的历史
    【转】java与.net比较学习系列(3) 基本数据类型和类型转换
  • 原文地址:https://www.cnblogs.com/lijilai-oi/p/12622307.html
Copyright © 2011-2022 走看看