#58. 【WC2013】糖果公园

这篇博客介绍了如何解决一个关于树上路径权值和的查询问题,即给定一棵节点颜色的树,求某路径上点的权值和。权值计算涉及到节点颜色、经过颜色的次数与特定w值的乘积。文章提到,这个问题可以使用树上莫队算法来解决,通过分块处理子树,并确保在边界移动时避免重复操作。作者提供了详细的操作步骤和注意事项,包括处理边界移动时避免lca(最低公共祖先)的重复操作,以及保持dfs序的重要性。

 

给出一棵n个节点的树,每个节点都有一种颜色c[i]。

q次询问[l,r]路径上的点的权值和vjwi。每一个节点的权值就是当前节点的颜色值v[c[i]]和从l到该节点经过该颜色的次数num[c[i]]对应的w值w[num[c[i]]之间的乘积(很绕,看题目描述吧,懒得描述了)

 

这是树上莫队。分块是对子树进行。如果一个某一个子树的size满足块大小,则分成一块。

void dfs1(int t, int f){
	sz[t]=1;
	fa[t] = f;
	dp[t] = dp[f]+1;
	dfn[t]=++clk;
	st.push(t);
	int cur_p = st.size();
	for(int v : g[t]){
		if(v==f)continue;
		dfs1(v,t);
		if(sz[v] > sz[hs[t]]) hs[t] = v;
		sz[t]+=sz[v];
		if(st.size() - cur_p >= block_size){
			//get one block
			while(st.size() > cur_p){
				block_num[st.top()] = cur_block;
				st.pop();
			}
			cur_block++;
		}
	}
	if(t==1){
		while(!st.empty()){
			block_num[st.top()] = cur_block;
			st.pop();
		}
		cur_block++;
	}
}

最后就是正常的莫队操作了。

 

树上莫队的难点在于[l,r]的边界移动。

假设从[l,r]移动到[l',r'], 将l移动到l'或者r移动到r'时,对树上的标记取反(异或)来判断是否加入或者删除。

但是这里会有重复操作,见https://www.luogu.com.cn/blog/dedicatus545/solution-p4074

因此需要当l走到l'的时候,lca(l,l')是不操作的。r到r'同理。

void move(int old_x, int new_x){
	if(dp[old_x] < dp[new_x]) swap(old_x,new_x);
	while(dp[old_x]!=dp[new_x]){
		change(old_x);
		old_x = fa[old_x];
	}
	while(old_x != new_x){
		change(old_x);
		change(new_x);
		old_x = fa[old_x];
		new_x = fa[new_x];
	}
}

最后将(l,r)的lca和[l',r']的lca取反保证他们只操作一次。

for(int i = 0; i < q.size();++i){
	    if(dfn[q[i].l]>dfn[q[i].r]) swap(q[i].l,q[i].r);
		int f = lca(l,r);
		change(f);
		while(cur<q[i].t){
			cur++;
			int pos = modi[cur].pos;
			int old_c = modi[cur].old_c;
			int new_c = modi[cur].c;
			change(pos, old_c, new_c);
		}
		while(cur>q[i].t){
			int pos = modi[cur].pos;
			int old_c = modi[cur].old_c;
			int new_c = modi[cur].c;
			change(pos, new_c,old_c);
			cur--;
		}

		if(l!=q[i].l) {
			move(l,q[i].l);
			l = q[i].l;
		}
		if(r!=q[i].r){
			move(r,q[i].r);
			r = q[i].r;
		}

		f = lca(l,r);
		change(f);
		ans[q[i].id] = sum;
	}

另外(l,r)需要保证他们的dfs序。

完整代码

int block_size;

struct query{
	int l,r,id;
	int block;
	int r_block;
	int t;
	bool operator<(const query &q) const{
		if(block != q.block) return block<q.block;
		if(r_block == q.r_block) return id < q.id;
		return r_block<q.r_block;
	}
}q[N];

struct Revise{ 
	int pos, c, old_c; 
};

int v[N],w[N];
int c[N], num[N],old[N];
ll ans[N];
ll sum;
stack<int> st;
int cur_block,clk;
int hs[N],sz[N], block_num[N];
int fa[N],top[N],dp[N],vis[N],dfn[N];
vector<int> g[N];

void dfs1(int t, int f){
	sz[t]=1;
	fa[t] = f;
	dp[t] = dp[f]+1;
	dfn[t]=++clk;
	st.push(t);
	int cur_p = st.size();
	for(int v : g[t]){
		if(v==f)continue;
		dfs1(v,t);
		if(sz[v] > sz[hs[t]]) hs[t] = v;
		sz[t]+=sz[v];
		if(st.size() - cur_p >= block_size){
			//get one block
			while(st.size() > cur_p){
				block_num[st.top()] = cur_block;
				st.pop();
			}
			cur_block++;
		}
	}
	if(t==1){
		while(!st.empty()){
			block_num[st.top()] = cur_block;
			st.pop();
		}
		cur_block++;
	}
}

void dfs2(int t, int f){
	top[t] = f;
	if(!hs[t]) return;
	dfs2(hs[t],f);
	for(int v : g[t]){
		if(v==fa[t] || v == hs[t])continue;
		dfs2(v,v);
	}
}

int lca(int x, int y){
	if(dp[x] < dp[y]) swap(x,y);
	while(top[x] != top[y]){
		if(dp[top[x]] >dp[top[y]]) x = fa[top[x]];
		else y = fa[top[y]];
	}
	return dp[x] < dp[y]? x:y;
}

void del(int idx){
	sum -= 1ll*w[num[idx]] * v[idx];
	num[idx]--;
}

void add(int idx){
	num[idx]++;
	sum += 1ll*w[num[idx]] * v[idx];
}

void change(int pos, int old_c, int new_c){
	if(vis[pos]){
		del(old_c);
		add(new_c);
	}
	c[pos] = new_c;
}

void change(int idx){
	if(vis[idx]){
		del(c[idx]);
	}
	else {
		add(c[idx]);
	}
	vis[idx]^=1;
}

void move(int old_x, int new_x){
	if(dp[old_x] < dp[new_x]) swap(old_x,new_x);
	while(dp[old_x]!=dp[new_x]){
		change(old_x);
		old_x = fa[old_x];
	}
	while(old_x != new_x){
		change(old_x);
		change(new_x);
		old_x = fa[old_x];
		new_x = fa[new_x];
	}
}

int main(){
	int n,m,Q;
	scanf("%d%d%d",&n,&m,&Q);
	block_size = pow(n,2.0/3);
	clk = 0;
	fr(i,1,m+1) {
		sf("%d",&v[i]);
	}
	fr(i,1,n+1) {
		sf("%d",&w[i]);
	}
	fr(i,0,n-1){
		int u,v;
		sf("%d%d",&u,&v);
		g[u].pb(v);
		g[v].pb(u);
	}
	fr(i,1,n+1){
		sf("%d",&c[i]);
		old[i] = c[i];
	}
	dfs1(1,1);
	dfs2(1,1);
	vector<query> q;
	vector<Revise> modi;
	fr(i,0,Q){
		int t,x,y;
		sf("%d%d%d",&t,&x,&y);
		if(t==1){
			query qy;
			qy.l = x;qy.r = y;
			qy.id = q.size();
			qy.block = block_num[qy.l];
			qy.r_block = block_num[qy.r];
			qy.t = modi.size()-1;
			q.pb(qy);
		}
		else {
			Revise r;
			r.pos = x;r.c = y;
			r.old_c = old[r.pos];
			modi.pb(r);
			old[r.pos] = r.c;
		}
	}

    sort(q.begin(),q.end());
	sum = 0;
	int l = 1, r = 1;
	change(l);
	int cur = -1;
	for(int i = 0; i < q.size();++i){
	    if(dfn[q[i].l]>dfn[q[i].r]) swap(q[i].l,q[i].r);
		int f = lca(l,r);
		change(f);
		while(cur<q[i].t){
			cur++;
			int pos = modi[cur].pos;
			int old_c = modi[cur].old_c;
			int new_c = modi[cur].c;
			change(pos, old_c, new_c);
		}
		while(cur>q[i].t){
			int pos = modi[cur].pos;
			int old_c = modi[cur].old_c;
			int new_c = modi[cur].c;
			change(pos, new_c,old_c);
			cur--;
		}

		if(l!=q[i].l) {
			move(l,q[i].l);
			l = q[i].l;
		}
		if(r!=q[i].r){
			move(r,q[i].r);
			r = q[i].r;
		}

		f = lca(l,r);
		change(f);
		ans[q[i].id] = sum;
	}
	
	fr(i,0,q.size()){
		printf("%lld\n",ans[i]);
	}
}

 

<think>我们被要求教授用户关于 du, ln, rm, cp, mv, find, cat, more/less/head/tail, wc, vi 这些命令的使用。 根据提供的引用,我们可以参考一些已有的说明(如tail、echo、whereis、man等),但用户的问题是关于上述特定命令的。 我们将按照命令逐个解释,包括基本用法和常用选项,并尽量结合引用中提到的相关知识点(如使用man查看手册)。 注意:用户要求是简单命令的使用,所以不需要过于深入,但需要清晰。 结构:每个命令单独一个小节,包含: 1. 命令简介 2. 基本语法 3. 常用选项和示例 4. 提示(如查看帮助等) 由于用户引用了多个资料,我们在适当的地方可以提到如何获取更多帮助(如man命令)。 注意:引用[3]提到了man命令,我们可以建议用户对每个命令使用man查看详细帮助。 我们将按照以下顺序介绍: du, ln, rm, cp, mv, find, cat, more/less/head/tail, wc, vi 开始编写回答。 </think>以下是对Linux常用命令的详细说明,结合了文件查看、目录操作和文本编辑等核心功能: ### 1. **`du` - 查看磁盘使用空间** ```bash du -sh * # 显示当前目录各文件/文件夹大小(-s汇总,-h人性化单位) du -h --max-depth=1 /opt # 查看/opt目录下一级子目录大小 ``` - **常用选项**:`-h`(易读格式), `-s`(显示总计), `--max-depth=N`(目录深度) --- ### 2. **`ln` - 创建文件链接** ```bash ln source.txt hardlink # 创建硬链接(共享inode) ln -s source.txt symlink # 创建软链接(类似快捷方式) ``` - **区别**:硬链接不能跨分区,删除源文件后硬链接仍可用;软链接可跨分区,源文件删除后失效[^4] --- ### 3. **`rm` - 删除文件/目录** ```bash rm file.txt # 删除文件 rm -r dir/ # 递归删除目录 rm -f *.log # 强制删除不提示(-f) ``` - **注意**:`rm -rf /` 会清空整个系统!慎用`-f` --- ### 4. **`cp`/`mv` - 复制与移动** ```bash cp file.txt backup/ # 复制文件 cp -r dir1/ dir2/ # 递归复制目录(-r) mv old.txt new.txt # 重命名文件 mv *.log /archive/ # 移动文件到目录 ``` --- ### 5. **`find` - 文件搜索** ```bash find /home -name "*.conf" # 按名称搜索 find . -size +10M # 查找>10MB的文件 find /var -mtime -7 # 查找7天内修改的文件 ``` - **高级用法**:`-exec`参数可联动其他命令,如`find . -name "*.tmp" -exec rm {} \;` --- ### 6. **`cat`/`head`/`tail` - 文件查看** ```bash cat file.txt # 显示整个文件 head -n 20 file.log # 显示前20行 tail -f /var/log/syslog # 实时追踪日志追加内容(-f)[^1] ``` - **组合技巧**:`cat file.txt | head -n 100 | tail -n 50`(显示51-100行) --- ### 7. **`more`/`less` - 分页查看** ```bash less large_file.log # 交互式查看(支持上下翻页/搜索) more +100 file.txt # 从第100行开始显示 ``` - **对比**:`less`功能更强大,支持反向搜索(`?keyword`)[^5] --- ### 8. **`wc` - 文本统计** ```bash wc -l access.log # 统计行数(-l) wc -w report.txt # 统计单词数(-w) wc -c binaryfile # 统计字节数(-c) ``` --- ### 9. **`vi` - 文本编辑器** ```bash vi file.txt # 打开文件 ``` - **基础操作**: - `i`进入编辑模式,`ESC`返回命令模式 - `:wq`保存退出,`:q!`强制退出不保存 - `/search`搜索文本,`dd`删除当前行 --- ### 学习建议 ```bash man rm # 查看命令完整手册[^3] whereis cp # 查找命令路径[^2] alias l=&#39;ls -lh&#39; # 创建命令别名[^2] ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值