【uoj58】 WC2013—糖果公园

http://uoj.ac/problem/58 (题目链接)

题意:给定一棵树,每个点有一个颜色,提供两种操作: 
   1.询问两点间路径上的Σv[a[i]]*w[k],其中a[i]代表这个点的颜色,k表示这个点是这种颜色第k次出现 
   2.修改某个点的颜色

Solution 
  带修改树上莫队。 
  按左端点所在块为第一关键字,右端点所在块为第二关键字,时间为第三关键字,排序。可能会有疑问可不可以以右端点dfs序为第二关键字?这里我们为了突出第三关键字的作用,选择以右端点所在块为第二关键字。每个节点的dfs序都不同,如果以dfs序为第二关键字的话,第三关键字就没用了。当然这样写也不是不行,但时间会略长。 


  然后进行树上莫队,每次询问经过修改或逆修改来使时间倒流或前进。 
  复杂度证明,很好理解。 
  

复杂度证明: 
  设block_num为块数,block_size为块的大小,则有block_num×block_size=n,在证明中我们假设n,q同阶。 
  设块对(block_i,block_j),易知这样的块对不会超过block_num2个。 
  对于块对内的操作:我们考虑总复杂度,左端点共移动至多O(q×block_size),右端点亦是。时间共移动至多O(block_num2×q)。故这一部分的复杂度为O(n×(block_size+block_num2))。 
  对于块与块之间的操作,不超过block_num2次:左端第移动一次,最多O(n),右端点亦是如此。时间最多移动O(q)=O(n)。故这一部分复杂度为O(block_num2×n)。 
故总复杂度为O(n×(block_size+block_num2))。 
  可以证明当block_size=n2/3时,block_num=n1/3,复杂度最优,为O(n5/3)。

代码:

// uoj58
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#define MOD 1000000007
#define inf 2147483640
#define LL long long
#define free(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout);
using namespace std;
inline LL getint() {
    LL x=0,f=1;char ch=getchar();
    while (ch>‘9‘ || ch<‘0‘) {if (ch==‘-‘) f=-1;ch=getchar();}
    while (ch>=‘0‘ && ch<=‘9‘) {x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}

const int maxn=100010;
struct edge {int to,next;}e[maxn<<2];
struct ask {int u,v,id,pre,t;}a1[maxn],a2[maxn];
LL res[maxn],ans;
int pos[maxn],v[maxn],w[maxn],dfn[maxn],bin[20],fa[maxn][20],deep[maxn],st[maxn],p[maxn],vis[maxn],c[maxn],pre[maxn],head[maxn];
int block,blonum,n,m,q,cnt,cnt1,cnt2,top;

void insert(int u,int v) {
    e[++cnt].to=v;e[cnt].next=head[u];head[u]=cnt;
    e[++cnt].to=u;e[cnt].next=head[v];head[v]=cnt;
}
bool cmp(ask a,ask b) {
    if (pos[a.u]==pos[b.u] && pos[a.v]==pos[b.v]) return a.t<b.t;
    if (pos[a.u]==pos[b.u]) return pos[a.v]<pos[b.v];
    return pos[a.u]<pos[b.u];
}
int dfs(int x) {
    int size=0;
    dfn[x]=++cnt;
    for (int i=1;i<20;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
    for (int i=head[x];i;i=e[i].next) if (e[i].to!=fa[x][0]) {
            deep[e[i].to]=deep[x]+1;
            fa[e[i].to][0]=x;
            size+=dfs(e[i].to);
            if (size>=block) {
                blonum++;
                while (size--) pos[st[top--]]=blonum;
                size=0;
            }
        }
    st[++top]=x;
    return size+1;
}
void work(int x) {
    if (!vis[x]) {vis[x]=1;p[c[x]]++;ans+=(LL)w[p[c[x]]]*v[c[x]];}
    else {vis[x]=0;ans-=(LL)w[p[c[x]]]*v[c[x]];p[c[x]]--;}
}
void modify(int x,int y) {
    if (vis[x]) {
        work(x);
        c[x]=y;
        work(x);
    }
    else c[x]=y;
}
void solve(int x,int y) {
    while (x!=y) {
        if (deep[x]<deep[y]) work(y),y=fa[y][0];
        else work(x),x=fa[x][0];
    }
}
int lca(int x,int y) {
    if (deep[x]<deep[y]) swap(x,y);
    int t=deep[x]-deep[y];
    for (int i=0;bin[i]<=t;i++) if (bin[i]&t) x=fa[x][i];
    for (int i=19;i>=0;i--)
        if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    return x==y?x:fa[x][0];
}
int main() {
    bin[0]=1;for (int i=1;i<20;i++) bin[i]=bin[i-1]<<1;
    scanf("%d%d%d",&n,&m,&q);
    for (int i=1;i<=m;i++) scanf("%d",&v[i]);
    for (int i=1;i<=n;i++) scanf("%d",&w[i]);
    for (int i=1;i<n;i++) {
        int u,v;
        scanf("%d%d",&u,&v);
        insert(u,v);
    }
    for (int i=1;i<=n;i++) scanf("%d",&c[i]),pre[i]=c[i];
    block=(int)pow(n,0.6);
    cnt=0;dfs(1);
    cnt1=0,cnt2=0;
    for (int i=1;i<=q;i++) {
        int x,u,v;
        scanf("%d%d%d",&x,&u,&v);
        if (x) {
            cnt1++;
            if (dfn[u]>dfn[v]) swap(u,v);
            a1[cnt1].u=u;a1[cnt1].v=v;a1[cnt1].id=cnt1;a1[cnt1].t=cnt2;
        }
        else {
            cnt2++;
            a2[cnt2].u=u;a2[cnt2].v=v;a2[cnt2].pre=pre[u];pre[u]=v;
        }
    }
    sort(a1+1,a1+cnt1+1,cmp);
    for (int i=1;i<=a1[1].t;i++) modify(a2[i].u,a2[i].v);
    solve(a1[1].u,a1[1].v);
    int t=lca(a1[1].u,a1[1].v);
    work(t);
    res[a1[1].id]=ans;
    work(t);
    for (int i=2;i<=cnt1;i++) {
        for (int j=a1[i-1].t+1;j<=a1[i].t;j++) modify(a2[j].u,a2[j].v);
        for (int j=a1[i-1].t;j>a1[i].t;j--) modify(a2[j].u,a2[j].pre);
        solve(a1[i-1].u,a1[i].u);
        solve(a1[i-1].v,a1[i].v);
        t=lca(a1[i].u,a1[i].v);
        work(t);
        res[a1[i].id]=ans;
        work(t);
    }
    for (int i=1;i<=cnt1;i++) printf("%lld\n",res[i]);
    return 0;
}

  

时间: 2024-10-12 11:37:11

【uoj58】 WC2013—糖果公园的相关文章

WC2013 糖果公园

COGS 1817. [WC2013]糖果公园 http://www.cogs.pro/cogs/problem/problem.php?pid=1817 ★★★☆   输入文件:park.in   输出文件:park.out   简单对比时间限制:8 s   内存限制:512 MB [题目描述] Candyland 有一座糖果公园,公园里不仅有美丽的风景.好玩的游乐项目,还有许多免费糖果的发放点,这引来了许多贪吃的小朋友来糖果公园玩. 糖果公园的结构十分奇特,它由 n 个游览点构成,每个游览点

[bzoj 3052][wc2013]糖果公园

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3052 [wc2013]糖果公园 Time Limit: 200 Sec  Memory Limit: 512 MBSubmit: 1213  Solved: 609[Submit][Status][Discuss] Description Input Output Sample Input Sample Input Sample Output 84 131 27 84 树上莫队,把树分块,

bzoj 3052: [wc2013]糖果公园 带修改莫队

3052: [wc2013]糖果公园 Time Limit: 250 Sec  Memory Limit: 512 MBSubmit: 506  Solved: 189[Submit][Status] Description Input Output Sample Input Sample Input Sample Output 84 131 27 84 HINT 本来这道题想到了莫队算法,但是看到带修改就直接放弃了.结果看题解才发现带修改居然也能用莫队!!!之所以可以这样用,是因为修改的时间复

AC日记——[WC2013]糖果公园 cogs 1817

[WC2013]糖果公园 思路: 带修改树上莫队(模板): 来,上代码: #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define maxn 100005 #define ll long long struct QueryType { ll u,v,t,id;

bzoj 3052: [wc2013]糖果公园(带修改的树上莫队)

3052: [wc2013]糖果公园 Time Limit: 200 Sec  Memory Limit: 512 MB Submit: 892  Solved: 425 [Submit][Status][Discuss] Description Input Output Sample Input Sample Input Sample Output 84 131 27 84 HINT Source [Submit][Status][Discuss] 题解:bzoj 2120 和 bzoj 37

【BZOJ】3052: [wc2013]糖果公园 树分块+待修改莫队算法

[题目]#58. [WC2013]糖果公园 [题意]给定n个点的树,m种糖果,每个点有糖果ci.给定n个数wi和m个数vi,第i颗糖果第j次品尝的价值是v(i)*w(j).q次询问一条链上每个点价值的和或修改一个点的糖果ci.n,m,q<=10^5. [算法]树分块+带修改莫队算法 [题解]参考:WC 2013 糖果公园 park 题解 by vfleaking 首先树分块,参考王室联邦的方法.确定块大小为B,一遍DFS可以分成若干大小为[B,3B]的块,性质是块内两点距离至多为B. 定义(x,

[BZOJ3052][UOJ#58][WC2013]糖果公园

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

【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