zoukankan      html  css  js  c++  java
  • @uoj


    @description@

    有一棵有根树,根为 1,点有点权。
    现在有 m 次操作,操作有 3 种:
    1 x y w,将 x 到 y 的路径上的点点权加上 w (其中 w=±1);
    2 x y,询问在 x 到 y 的路径上有多少个点点权 >0;
    3 x,询问在 x 的子树里的点有多少个点点权 >0。

    输入格式
    第一行三个数 n,m,T,表示树的结点个数,操作个数,和是否加密。
    接下来 n−1 行,每行 2 个数 x y,表示结点 x 和 y 之间有一条边。
    接下来一行 n 个数,第 i 个数表示结点 i 的初始点权。
    接下来 m 行,每行格式见题目描述。
    如果 T=1,则这 m 行读入的每个 x,y 都需要异或 last_ans 才能得到真实的输入,其中 last_ans 表示上一次询问操作的答案,如果不存在上一次询问操作则为 0​。

    输出格式
    对于每个询问操作,输出一行表示答案。

    样例一
    input
    5 5 0
    1 2
    1 3
    3 4
    3 5
    1 0 0 0 0
    2 2 5
    3 3
    1 2 5 1
    2 2 5
    3 3
    output
    1
    0
    4
    2

    限制与约定
    对于所有数据,1≤n≤10^5,1≤m≤10^5,−10^9≤ 点权 ≤10^9。

    @solution@

    不妨先考虑链的情况。

    对区间的值域进行修改与查询,除了分块以外,起码我是没有想到其他方法。
    修改时整块 tag,散块暴力重构排序;查询时整块二分,散块暴力。
    得到了一个 (O(nsqrt{n}log n)) 的算法。

    至于上树。树分块?没听说过,啥东西呀。
    我们可以通过树链剖分转成 log 个不相交的区间修改查询。
    得到了一个连暴力都不如的 (O(nsqrt{n}log^2 n)) 的算法。

    别慌,我们来尝试优化一下,能否去掉分块本身的那个 log。
    一种空间消耗较大的方法使用桶。注意到当值域的绝对值 |ai| > m 时,多的部分是没有用的。于是我们可以把值域缩到 -m ~ m 之间。
    然后就可以对于每个块开一个大小为 2*m 的桶。每次整块 ±1 时,整个块的答案的变化量可以 O(1) 算。
    这道题可能要开 short int 才不会被卡空间。

    另一种方法就是直接优化原先的做法。
    排序可以将被修改的区间与没有被修改的区间先裂开,更改,再进行归并。就没有 log 的存在了。
    整块修改时维护一个指针方便计算答案的变化量。为了做到 O(1) 移动指针,需要一次跳过值相同的所有数,再维护一下值相同的最左与最右。

    这样就优化成了 (O(nsqrt{n}log n))。不过还是有点玄,我们再优化一下。
    注意到树链剖分将一次操作转成了 log 个不相交的区间操作,因此一次最多涉及 (O(sqrt{n})) 个整块,而散块可能有 (O(log n)) 个。
    因此散块的复杂度比整块大。我们把块调小一点,变成 (O(sqrt{nlog n})) 个块,就可以将复杂度有效均衡在 (O(sqrt{nlog n}))

    @accepted code@

    //用桶的方法 AC 的
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int MAXN = 100002;
    const int BLOCK = 105;
    struct edge{
    	int to; edge *nxt;
    }edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt=&edges[0];
    void addedge(int u, int v) {
    	edge *p = (++ecnt);
    	p->to = v, p->nxt = adj[u], adj[u] = p;
    	p = (++ecnt);
    	p->to = u, p->nxt = adj[v], adj[v] = p;
    }
    int siz[MAXN + 5], dep[MAXN + 5], hvy[MAXN + 5], fa[MAXN + 5];
    void dfs1(int x, int f) {
    	fa[x] = f, siz[x] = 1, dep[x] = dep[f] + 1, hvy[x] = 0;
    	for(edge *p=adj[x];p;p=p->nxt) {
    		if( p->to == f ) continue;
    		dfs1(p->to, x);
    		siz[x] += siz[p->to];
    		if( siz[p->to] > siz[hvy[x]] )
    			hvy[x] = p->to;
    	}
    }
    int top[MAXN + 5], tid[MAXN + 5], dfn[MAXN + 5], dcnt;
    void dfs2(int x, int tp) {
    	top[x] = tp, tid[x] = (++dcnt), dfn[dcnt] = x;
    	if( !hvy[x] ) return ;
    	dfs2(hvy[x], tp);
    	for(edge *p=adj[x];p;p=p->nxt)
    		if( p->to != fa[x] && p->to != hvy[x] )
    			dfs2(p->to, p->to);
    }
    int n, m, T;
    int a[MAXN + 5], id[MAXN + 5];
    int l[MAXN + 5], r[MAXN + 5], tg[MAXN + 5], res[MAXN + 5];
    short int cnt[1000][2*MAXN + 5];
    void insert(int k, int x) {cnt[x][k]++; if( k > tg[x] ) res[x]++;}
    void erase(int k, int x) {cnt[x][k]--; if( k > tg[x]) res[x]--;}
    void add(int x) {res[x] += cnt[x][tg[x]], tg[x]--;}
    void remove(int x) {tg[x]++, res[x] -= cnt[x][tg[x]];}
    void build() {
    	int bcnt = 0;
    	for(int i=1;i<=n;i++) {
    		if( (i - 1) % BLOCK == 0 )
    			bcnt++, l[bcnt] = i, tg[bcnt] = MAXN, res[bcnt] = 0;
    		r[bcnt] = i, insert(a[dfn[i]], id[i] = bcnt);
    	}
    }
    void update(int &x) {
    	if( x < 0 ) x = 0;
    	if( x > 2*MAXN ) x = 2*MAXN;
    }
    void Amodify(int le, int ri, int w) {
    	if( id[le] == id[ri] ) {
    		int p = id[le];
    		for(int i=le;i<=ri;i++) {
    			erase(a[dfn[i]], p);
    			a[dfn[i]] += w, update(a[dfn[i]]);
    			insert(a[dfn[i]], p);
    		}
    	}
    	else {
    		int p = id[le], q = id[ri];
    		for(int i=le;i<=r[p];i++) {
    			erase(a[dfn[i]], p);
    			a[dfn[i]] += w, update(a[dfn[i]]);
    			insert(a[dfn[i]], p);
    		}
    		for(int i=p+1;i<=q-1;i++)
    			( w == 1 ) ? add(i) : remove(i);
    		for(int i=l[q];i<=ri;i++) {
    			erase(a[dfn[i]], q);
    			a[dfn[i]] += w, update(a[dfn[i]]);
    			insert(a[dfn[i]], q);
    		}
    	}
    }
    int Aquery(int le, int ri) {
    	int ret = 0;
    	if( id[le] == id[ri] ) {
    		int p = id[le];
    		for(int i=le;i<=ri;i++)
    			ret += (a[dfn[i]] > tg[p]);
    	}
    	else {
    		int p = id[le], q = id[ri];
    		for(int i=le;i<=r[p];i++)
    			ret += (a[dfn[i]] > tg[p]);
    		for(int i=p+1;i<=q-1;i++)
    			ret += res[i];
    		for(int i=l[q];i<=ri;i++) 
    			ret += (a[dfn[i]] > tg[q]);
    	}
    	return ret;
    }
    void modify(int x, int y, int w) {
    	while( top[x] != top[y] ) {
    		if( dep[top[x]] < dep[top[y]] ) swap(x, y);
    		Amodify(tid[top[x]], tid[x], w);
    		x = fa[top[x]];
    	}
    	if( dep[x] < dep[y] ) swap(x, y);
    	Amodify(tid[y], tid[x], w);
    }
    int query(int x, int y) {
    	int ret = 0;
    	while( top[x] != top[y] ) {
    		if( dep[top[x]] < dep[top[y]] ) swap(x, y);
    		ret += Aquery(tid[top[x]], tid[x]);
    		x = fa[top[x]];
    	}
    	if( dep[x] < dep[y] ) swap(x, y);
    	ret += Aquery(tid[y], tid[x]);
    	return ret;
    }
    int query(int x) {
    	return Aquery(tid[x], tid[x] + siz[x] - 1);
    }
    int main() {
    	scanf("%d%d%d", &n, &m, &T);
    	for(int i=1;i<n;i++) {
    		int x, y; scanf("%d%d", &x, &y);
    		addedge(x, y);
    	}
    	for(int i=1;i<=n;i++)
    		scanf("%d", &a[i]), a[i] += MAXN, update(a[i]);
    	dfs1(1, 0), dfs2(1, 1), build();
    	int last_ans = 0;
    	for(int i=1;i<=m;i++) {
    		int op; scanf("%d", &op);
    		if( op == 1 ) {
    			int x, y, w; scanf("%d%d%d", &x, &y, &w);
    			modify(x ^ last_ans, y ^ last_ans, w);
    		}
    		else if( op == 2 ) {
    			int x, y; scanf("%d%d", &x, &y);
    			last_ans = query(x ^ last_ans, y ^ last_ans);
    			printf("%d
    ", last_ans), last_ans *= T;
    		}
    		else {
    			int x; scanf("%d", &x);
    			last_ans = query(x ^ last_ans);
    			printf("%d
    ", last_ans), last_ans *= T;
    		}
    	}
    }
    

    @details@

    写完桶的方法发现被 hack 数据卡内存。
    然后一怒之下换成重构的方法,调了一下午终于过了(我树链剖分要是再搞混点编号与 dfs 序中的编号我就***)。

    然后有人告诉我 short int 能过。
    。。。

  • 相关阅读:
    分享:十Python之Http Web服务(网页抓取二)
    分享:C语言打印long long类型
    分享:svn 添加*.so等文件
    visual assist x 安装
    设计模式之MVC模式
    php一个递归读取目录文件脚本
    转:javax.swing.JFrame中使用jpanel来布局
    alter table 总结
    php 函数:func_get_args()、func_get_arg()与func_num_args()
    Visual Assist X简介
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11712604.html
Copyright © 2011-2022 走看看