2024.11.16模拟赛(*^▽^*)

总结:日常犯困,日常去厕所清醒,日常疯狂调试,不日常四个半小时的模拟赛。打了T1的60分暴力+特殊样例,T4的40分暴力+特殊样例,但是T1不知道为什么\(dfs\)爆栈了,所以没骗到特殊样例的分,T4特殊样例式子推错,也没骗到分,所以最后T1 30分,T4 20分,共50分,挂了50分

关于T1:四个人,想了四个半小时,摸到了正解的边,但不多……

题目小链接


T1【细胞】

题目大意:

给定一棵以1为根、共\(n\)个节点的树,开始每个节点都有一个点权,每过一秒,每个点的权值会变成它父节点的权值。

现在给出\(m\)个操作,每次操作可以查询当前时间点\(v\)的权值,或给点\(v\)的权值加\(k\)(先转移再增加)。特别地,若\(v\)为叶子结点,那么每次它的权值会加上它父节点的权值。\((1\leqslant n\leqslant 5\times 10^{5})\)

解题思路:

直接模拟的复杂度为\(O(nm)\),不可以。我们可以想到,对于\(t\)时间询问的点\(v\),会直接对该点产生贡献的祖先点\(u\)满足\(dep_{u}=dep_{v}-t\)(若\(v\)是叶子节点,那么产生贡献的点满足\(dep_{u}\geqslant dep_{v}-t)\)

所以对于每次查询\(v\)就是要求会对该点产生贡献的点的权值和。但由于存在修改操作,所以是带修查询,区间查询+单点修改,想到树状数组维护。(什么?我还是不会树状数组……)

离线存储\(m\)个操作,跑一遍\(dfs\),若节点\(u\)存在修改操作,那么就相当于是刚开始在\(dup_{u}-t\)处有一权值(此时路径上的查询操作已经处理完,此次修改不会影响路径前面的查询,只会修改路径后的查询——但要注意回溯以及偏移量),若存在查询操作,查询区间和就行。(也有可能是单点查询哦?)

乖代码
#incIude <bits/stdc++.h>
#define int long long
#define pii pair<int,int>
#define mp make_pair
using namespace std;
const int N=5e5+5;//注意偏移量 
int n,m;
vector <int> tr[N];
vector <pii> add[N];//时间,修改
vector <int> que[N]; 
int cnt[N<<1];
int ans[N];
queue <int> ot;

int dep[N];
int lowbit(int x) { return x&-x; }
void _update(int t,int x)
{
	while (t<=(N<<1))
	{
		cnt[t]+=x;
		t+=lowbit(t);
	}
}
int _sum(int x)
{
	int res=0;
	while (x)
	{
		res+=cnt[x];
		x-=lowbit(x);
	}
	return res;
}
void dfs(int x,int _fa)
{
	bool flag=false;
	if (tr[x].size()==1) flag=true;//叶子结点 
	dep[x]=dep[_fa]+1;
	
	int _size1=add[x].size();
	for (int i=0;i<_size1;i++) _update(dep[x]-add[x][i].first+N,add[x][i].second);//先单点修改 
	
	int _size2=que[x].size();
	for (int i=0;i<_size2;i++)
	{
		int p=que[x][i];//查询时间点 
		if (flag) ans[p]+=_sum(dep[x]+N)-_sum(dep[x]-p+N-1);
		else ans[p]+=_sum(dep[x]-p+N)-_sum(dep[x]-p+N-1);
	}
	
	int _size3=tr[x].size();
	for (int i=0;i<_size3;i++)
	{
		int v=tr[x][i];
		if (v==_fa) continue;
		dfs(v,x);
	}
	
	for (int i=0;i<_size1;i++) _update(dep[x]-add[x][i].first+N,-add[x][i].second);//回溯
}
signed main()
{
	cin>>n>>m;
	for (int i=1,x,y;i<n;i++)//存储树 
	{
		cin>>x>>y;
		tr[x].push_back(y);
		tr[y].push_back(x);
	}
	for (int i=1,x;i<=n;i++) cin>>x,add[i].push_back({0,x});//单点修改 
	for (int i=1;i<=m;i++)
	{
		char op;
		int v,k;
		cin>>op;
		if (op=='+') cin>>v>>k,add[v].push_back({i,k});//单点修改 
		else cin>>v,que[v].push_back(i),ot.push(i);//区间查询 
	}
	dfs(1,0);
	while (!ot.empty()) cout<<ans[ot.front()]<<'\n',ot.pop();
	return 0;
}

下次我再写“\(\times 2\)”而不是“>>\(1\)”导致 \(TLE\) 我就……一天不玩原神


posted @ 2024-11-17 17:03  还是沄沄沄  阅读(5)  评论(0编辑  收藏  举报