题解 P5528【[Ynoi2012] WC, THUWC, CTSC 与 APIO2017】
题意
子树距离 加,单点求值。
思路
暴力万岁!
考虑将树转化为序列,一个点 的子树可表示为 序列中的一个连续段,设为 。
则原题转化为区间 加,单点求值,这个很像[Ynoi2011] 初始化,没做过的可以做一下。考虑维护差分,然后根号分治, 时 , 时暴力加即可。
struct data_structer{
int val1[sq+10][sq+10],val2[N];
inline void add(int x,int y,int v){
if(x<=sq){
val1[x][y]+=v;
}else{
for(int i=y;i<=deep;i+=x){
val2[i]+=v;
}
}
}
inline void clear(int x,int y){
if(x<=sq){
val1[x][y]=0;
}else{
for(int i=y;i<=deep;i+=x){
val2[i]=0;
}
}
}
inline int query(int x){
int ans=val2[x];
for(int i=1;i<=sq;i++){
ans+=val1[i][x%i];
}
return ans;
}
}t;
考虑所有修改对询问的贡献的条件:
- :修改 的时间前于询问 ;
- :询问点在修改区间内;
- :满足同余条件。
接下来就可以 做了。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=3e5+10,sq=550;
int cnt,head[N],dep[N],l[N],r[N],deep,tim,ask;
struct Edge{
int to,nxt;
}a[N];
inline void add(int x,int y){
cnt++;
a[cnt].to=y;
a[cnt].nxt=head[x];
head[x]=cnt;
}
inline void dfs(int x){
tim++;
l[x]=tim;
deep=max(deep,dep[x]);
for(int i=head[x];i;i=a[i].nxt){
int t=a[i].to;
dep[t]=dep[x]+1;
dfs(t);
}
r[x]=tim;
}
struct data_structer{
int val1[sq+10][sq+10],val2[N];
inline void add(int x,int y,int v){
if(x<=sq){
val1[x][y]+=v;
}else{
for(int i=y;i<=deep;i+=x){
val2[i]+=v;
}
}
}
inline void clear(int x,int y){
if(x<=sq){
val1[x][y]=0;
}else{
for(int i=y;i<=deep;i+=x){
val2[i]=0;
}
}
}
inline int query(int x){
int ans=val2[x];
for(int i=1;i<=sq;i++){
ans+=val1[i][x%i];
}
return ans;
}
}t;
struct num{
int pos,x,y,opt,val;
};
int n,m,ans[N];
num b[N<<1],c[N<<1];
inline void cdq(int l,int r){
if(l==r) return;
int mid=l+r>>1;
cdq(l,mid),cdq(mid+1,r);
int q=l;
int k=l;
for(int i=mid+1;i<=r;i++){
while(b[q].pos<=b[i].pos&&q<=mid){
if(!b[q].opt)t.add(b[q].x,b[q].y,b[q].val);
c[k++]=b[q++];
}
if(b[i].opt){
ans[b[i].val]+=t.query(b[i].x);
}
c[k++]=b[i];
}
for(int i=l;i<q;i++){
if(!b[i].opt)t.clear(b[i].x,b[i].y);
}
for(;q<=mid;)c[k++]=b[q++];
for(int i=l;i<=r;i++){
b[i]=c[i];
}
}
int main(){
n=read(),m=read();
for(int i=2;i<=n;i++){
add(read(),i);
}
dfs(1);
cnt=0;
for(int i=1;i<=m;i++){
int opt=read(),a=read();
switch(opt){
case 1:{
int x=read(),y=read(),v=read();
b[++cnt]=(num){l[a],x,(dep[a]+y)%x,0,v};
b[++cnt]=(num){r[a]+1,x,(dep[a]+y)%x,0,-v};
break;
}
case 2:{
b[++cnt]=(num){l[a],dep[a],0,1,++ask};
break;
}
}
}
cdq(1,cnt);
for(int i=1;i<=ask;i++){
write(ans[i]);
putc('\n');
}
flush();
return 0;
}
然后您一交,只有 95pts。
我们发现这个算法的时间复杂度为 ,被卡常了。
然后这个做法就不能做了吗?不,我们可以卡常。
在 或 时特殊处理,同样根号分治维护,但是可以转换为全局减去未覆盖部分。
然后就能过了。
修改过后的主函数:
int main(){
n=read(),m=read();
for(int i=2;i<=n;i++){
add(read(),i);
}
dfs(1);
cnt=0;
for(int i=1;i<=m;i++){
int opt=read(),a=read();
switch(opt){
case 1:{
int x=read(),y=read(),v=read();
int c=(dep[a]+y)%x;
if(l[a]<=sq){
if(x<=sq){
val1[x][c]+=v;
}else{
for(int i=c;i<=deep;i+=x){
val2[i]+=v;
}
}
for(int i=1;i<l[a];i++){
if(dep[as[i]]%x==c){
val0[as[i]]-=v;
}
}
}else{
b[++cnt]=(num){l[a],x,c,0,v};
}
if(r[a]+sq>=n){
for(int i=r[a]+1;i<=n;i++){
if(dep[as[i]]%x==c){
val0[as[i]]-=v;
}
}
}else{
b[++cnt]=(num){r[a]+1,x,c,0,-v};
}
break;
}
case 2:{
b[++cnt]=(num){l[a],dep[a],0,1,++ask};
ans[ask]=val0[a]+val2[dep[a]];
for(int i=1;i<=sq;i++){
ans[ask]+=val1[i][dep[a]%i];
}
break;
}
}
}
cdq(1,cnt);
for(int i=1;i<=ask;i++){
write(ans[i]);
putc('\n');
}
flush();
return 0;
}
如果我之后被卡掉了,提醒我一下(
再见 qwq~
分类:
题解
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现