【BZOJ-3052】糖果公园 树上带修莫队算法

3052: [wc2013]糖果公园

Time Limit: 200 Sec  Memory Limit: 512 MB
Submit: 883  Solved: 419
[Submit][Status][Discuss]

Description

Input

Output

Sample Input

Sample Output

84
131
27
84

HINT

Source

Solution

树上带修莫队

本质还是树上莫队,详情可以转 BZOJ-3757苹果树

但是这里需要修改,就需要一些特殊的地方

首先DFS对树分块,没什么区别,只不过这里分块可以分得大一些,跑得快

把一个询问看成一个三元组$(a,b,t)$,$t$是询问的时间,这样对询问排序的时候,就是三关键字

然后在处理询问的时候,暴力处理修改,不过处理要分情况,如果经过则先对结果进行修改再修改数值,否则直接修改即可

并不是很详细,还是直接看VFleaKing的讲解吧ORZ VFK

启发:

莫队算法不仅可以处理不带修,同样可以处理带修的问题 (似乎还可以处理强制在线的?奇怪的姿势??)

分块的技巧有很多,应该根据实际情况去选择适合的块的大小

树上莫队的大体思路都比较类似,实际实现起来也非常像,遇到类似的问题可以如此考虑

平常得多做一些难写难调的花式题,使得码力++多看看神犇们的解题报告似乎是个不错的事

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define maxn 100010
#define maxm 100010
#define maxq 100010
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}
int n,m,Q,fk,knum,rt[maxn];long long V[maxm],W[maxn],an[maxn],C[maxn],ans;
struct Edgenode{int to,next;}edge[maxn<<1];
int head[maxn],cnt;
void add(int u,int v)
{cnt++;edge[cnt].next=head[u];head[u]=cnt;edge[cnt].to=v;}
void insert(int u,int v)
{add(u,v);add(v,u);}
int stack[maxn],top,dfsx,dfs[maxn],deep[maxn],father[maxn][25];
int DFS(int now)
{
    int size=0;
    dfs[now]=++dfsx;
    for (int i=1; i<=20; i++)
        if (deep[now]>=(1<<i)) father[now][i]=father[father[now][i-1]][i-1];
    for (int i=head[now]; i; i=edge[i].next)
        if (edge[i].to!=father[now][0])
            {
                deep[edge[i].to]=deep[now]+1;
                father[edge[i].to][0]=now;
                size+=DFS(edge[i].to);
                if (size>=fk)
                    {
                        knum++;
                        for (int j=1; j<=size; j++) rt[stack[top--]]=knum;
                        size=0;
                    }
            }
    stack[++top]=now;
    return size+1;
}
int LCA(int x,int y)
{
    if (deep[x]<deep[y]) swap(x,y);
    int dd=deep[x]-deep[y];
    for (int i=0; i<=20; i++)
        if (dd&(1<<i) && dd>=(1<<i)) x=father[x][i];
    for (int i=20; i>=0; i--)
        if (father[x][i]!=father[y][i])
            x=father[x][i],y=father[y][i];
    if (x==y) return x; else return father[x][0];
}
bool visit[maxn]; int num[maxn];
void Reverse(int x)
{
    if (visit[x]) {visit[x]=0; ans-=W[num[C[x]]]*V[C[x]]; num[C[x]]--;}
        else {visit[x]=1;  num[C[x]]++; ans+=W[num[C[x]]]*V[C[x]];}
//    printf("%d\n",ans);
}
void Change(int x,int y)
{
    if (visit[x]) Reverse(x),C[x]=y,Reverse(x);
        else C[x]=y;
}
void work(int x,int y)
{
    while (x!=y)
        if (deep[x]>deep[y]) Reverse(x),x=father[x][0];
            else Reverse(y),y=father[y][0];
}
struct Asknode
{
    int a,b,t,id;
    bool operator < (const Asknode & A) const
        {
            if (rt[a]==rt[A.a] && rt[b]==rt[A.b]) return t<A.t;
                else if (rt[a]==rt[A.a]) return rt[b]<rt[A.b];
            return rt[a]<rt[A.a];
        }
}q[maxq];int numq;
struct Changenode{int a,b,t,p;}ch[maxq];int numc,p[maxq];
int main()
{
    n=read(),m=read(),Q=read(); fk=pow(n,2.0/3)*0.5;
    for (int i=1; i<=m; i++) V[i]=read();
    for (int i=1; i<=n; i++) W[i]=read();
    for (int u,v,i=1; i<=n-1; i++) u=read(),v=read(),insert(u,v);
    for (int i=1; i<=n; i++) C[i]=read();
    for (int i=1; i<=n; i++) p[i]=C[i];

    DFS(1);
//    puts("OK");
//    for (int i=1; i<=n; i++) printf("%d %d %d %d\n",V[i],W[i],dfs[i],p[i]);
//    for (int i=1; i<=n; i++) printf("%d ",rt[i]); puts("");
//    puts("OK");
    while (top) rt[stack[top--]]=knum;    

    for (int i=1; i<=Q; i++)
        {
            int opt=read(),a=read(),b=read();
            if (opt) {if (dfs[a]>dfs[b]) swap(a,b); numq++;q[numq].a=a; q[numq].b=b; q[numq].t=numc; q[numq].id=numq;}
                else {numc++;ch[numc].a=a;ch[numc].b=b;ch[numc].t=i;ch[numc].p=p[a]; p[a]=b;}
        }
    sort(q+1,q+numq+1);
    //for (int i=1; i<=numq; i++) printf("%d %d %d %d\n",q[i].a,q[i].b,q[i].id,q[i].t);
    for (int i=1; i<=q[1].t; i++) Change(ch[i].a,ch[i].b);
    work(q[1].a,q[1].b);
    int T=LCA(q[1].a,q[1].b);
    Reverse(T); an[q[1].id]=ans; Reverse(T);
    for (int i=2; i<=numq; i++)
        {
            for(int j=q[i-1].t+1; j<=q[i].t; j++) Change(ch[j].a,ch[j].b);
            for(int j=q[i-1].t; j>q[i].t; j--) Change(ch[j].a,ch[j].p);
            work(q[i-1].a,q[i].a); work(q[i-1].b,q[i].b);
            T=LCA(q[i].a,q[i].b); Reverse(T); an[q[i].id]=ans; Reverse(T);
        }
    for (int i=1; i<=numq; i++) printf("%lld\n",an[i]);
    return 0;
}

看论文+写+调了一整个上午..1min30s跑完..成功卡住5人评测TAT‘‘ 吐槽一下BZOJ评测机..UOJ上就跑了20s..

时间: 2025-01-18 09:46:21

【BZOJ-3052】糖果公园 树上带修莫队算法的相关文章

bzoj4129 Haruna’s Breakfast 树上带修莫队+分块

题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4129 题解 考虑没有修改的序列上的版本应该怎么做: 弱化的题目应该是这样的: 给定一个序列,每次询问区间 \([l, r]\) 中元素的最小没有出现的自然数. 这个弱化的版本可以用离线+线段树二分水掉.但是这个做法显然不太好搬到树上做. 上面的弱化版还有一个莫队做法:可以用莫队维护出来每一个区间的每一个数的出现为次数.把出现过的数通过分块表示出来,于是查询的时候枚举每一个块,寻找第一个不满的

BZOJ4129(树上带修莫队)

树上莫队的基本思路是把树按dfs序分块,然后先按x所在块从小到大排序,再按y所在块从小到大排序,处理询问即可. 这道题带修改,再加一个时间维即可. 时间复杂度据说是$n^{\frac53}$,不知道是为什么. (块大小改成3也过了什么鬼..) #include <cstdio> #include <algorithm> using namespace std; const int N=50005,M=100005,B=1357,T=223; int n,m,e,x,y,t,tp,o

【Luogu P4074】[WC2013]糖果公园(树上带修改莫队)

题目描述 Candyland 有一座糖果公园,公园里不仅有美丽的风景.好玩的游乐项目,还有许多免费糖果的发放点,这引来了许多贪吃的小朋友来糖果公园游玩. 糖果公园的结构十分奇特,它由 \(n\) 个游览点构成,每个游览点都有一个糖果发放处,我们可以依次将游览点编号为 \(1\) 至 \(n\).有 \(n-1\) 条双向道路连接着这些游览点,并且整个糖果公园都是连通的,即从任何一个游览点出发都可以通过这些道路到达公园里的所有其它游览点. 糖果公园所发放的糖果种类非常丰富,总共有 \(m\) 种,

[UOJ #58][WC2013]糖果公园(树上带修改莫队)

Description Solution 树上带修改莫队…!VFK的题解写得很清楚啦 (我的程序为什么跑得这么慢…交的时候总有一种自己在卡测评的感觉…) #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #define MAXN 100005 typedef long l

[BZOJ 4129]Haruna’s Breakfast(树上带修改莫队)

Description Haruna每天都会给提督做早餐! 这天她发现早饭的食材被调皮的 Shimakaze放到了一棵 树上,每个结点都有一样食材,Shimakaze要考验一下她. 每个食材都有一个美味度,Shimakaze会进行两种操作: 1.修改某个结点的食材的美味度. 2.对于某条链,询问这条链的美味度集合中,最小的未出现的自然数是多少.即mex值. 请你帮帮Haruna吧. Solution 树上带修改莫队 统计答案的时候也分块查询,找到第一个没满的块开始一个一个找 #include<i

【带修莫队】【权值分块】bzoj3196 Tyvj 1730 二逼平衡树

这题用了三种算法写: 分块+二分:O(n*sqrt(n*log(n)) 函数式权值分块:O(n*sqrt(n)) 带修莫队+权值分块:O(n5/3) 结果……复杂度越高的实际上跑得越快……最后这个竟然进第一页了…… #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; int f,C; inline void R(int &

2120: 数颜色(带修莫队)

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2120 2120: 数颜色 Time Limit: 6 Sec  Memory Limit: 259 MBSubmit: 10514  Solved: 4398[Submit][Status][Discuss] Description 墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问.墨墨会像你发布如下指令: 1. Q L R代表询问你从第L支画笔到第R

CF940F Machine Learning(带修莫队)

首先显然应该把数组离散化,然后发现是个带修莫队裸题,但是求mex比较讨厌,怎么办?其实可以这样求:记录每个数出现的次数,以及出现次数的出现次数.至于求mex,直接暴力扫最小的出现次数的出现次数为0的正整数,就一句话,这样看似会超时,实际上是O(√n)的复杂度.为什么?假设存在出现1,2,...,x的出现次数,则Σi(1<=i<=x)<=n,即x*(x+1)<=2*n,所以x至多是√n级别.很多人再把出现次数分块,根本没必要.然后考虑把数组分块的块大小,每次移动左指针,为O(n*块大

莫队 + 带修莫队

莫队其实就是一个带优化的暴力,通过将区间询问按一定规则进行排序,从而优化过程,求出答案. 举一例子:(例子不具备权威性,只是让读者了解莫队是干啥的) /* 输入四个区间 1 4 初始条件,L= R = 0, 将R遍历到4 需要走4步 L走一步,共5次 4 8 继承上一次 L 和 R 的值,L从1到4 需要3次,R从4到8,需4次, 总计8次 2 9 同理继承, L回退2次, R前进一次 总计3次 1 2 同理,L回退1次,R回退7次 总计8次 如果直接暴力,计算机将要计算 5+8+3+8=24次