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\) 我就……一天不玩原神