CSU 1811 Tree Intersection

莫队算法,$dfs$序。

题目要求计算将每一条边删除之后分成的两棵树的颜色的交集中元素个数。

例如删除$u->v$,我们只需知道以$v$为$root$的子树中有多少种不同的颜色(记为$qq$),有多少种颜色达到了最多数量(记为$pp$),那么以$v$为$root$的子树与另一棵树的颜色交集中元素个数为$qq-pp$。

因为是计算子树上的量,所以可以将树转换成序列,每一个子树对应了序列中一段区间,具体计算只要用莫队算法分块就可以无脑的计算了。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<iostream>
using namespace std;
typedef long long LL;
const double pi=acos(-1.0),eps=1e-6;
void File()
{
    freopen("D:\\in.txt","r",stdin);
    freopen("D:\\out.txt","w",stdout);
}
template <class T>
inline void read(T &x)
{
    char c = getchar(); x = 0;while(!isdigit(c)) c = getchar();
    while(isdigit(c)) { x = x * 10 + c - ‘0‘; c = getchar();  }
}

const int maxn=200010;
struct Edge { int u,v,nx; bool f; }e[2*maxn];
int h[maxn],sz,w[maxn],cnt[maxn],pos[maxn],g[maxn];
int n,a[2*maxn],L[2*maxn],R[2*maxn];
struct Q { int L,R,id; }q[maxn];
int Ans[maxn],pp,qq;

void add(int u,int v)
{
    e[sz].u=u; e[sz].v=v; e[sz].nx=h[u];
    e[sz].f=0; h[u]=sz++;
}

void dfs(int x,int fa)
{
    sz++; a[sz]=w[x]; L[x]=sz;
    for(int i=h[x];i!=-1;i=e[i].nx)
    {
        if(e[i].v==fa) continue;
        e[i].f=1; dfs(e[i].v,x);
    }
    sz++; a[sz]=w[x]; R[x]=sz;
}

bool cmp(Q a,Q b) { if (pos[a.L]==pos[b.L]) return a.R<b.R; return a.L<b.L; }
void op1(int x) { if(cnt[a[x]]-g[a[x]]==1) pp--; if(g[a[x]]==0) qq--; }
void op2(int x) { if(g[a[x]]==cnt[a[x]]) pp++; if(g[a[x]]==1) qq++; }

int main()
{
    while(~scanf("%d",&n))
    {
        memset(cnt,sz=0,sizeof cnt);
        memset(h,-1,sizeof h);
        memset(g,0,sizeof g);
        memset(Ans,0,sizeof Ans);

        for(int i=1;i<=n;i++) { scanf("%d",&w[i]); cnt[w[i]]++; }
        for(int i=1;i<=n;i++) cnt[i]=2*cnt[i];

        for(int i=1;i<n;i++)
        {
            int u,v; scanf("%d%d",&u,&v);
            add(u,v); add(v,u);
        }
        sz=0; dfs(1,-1);

        for(int i=0;i<2*(n-1);i++)
        {
            if(e[i].f==0) continue;
            q[i/2].L=L[e[i].v]; q[i/2].R=R[e[i].v];
            q[i/2].id=i/2;
        }

        sz=sqrt(2*n); for(int i=1;i<=2*n;i++) pos[i]=i/sz;
        sort(q,q+(n-1),cmp);

        pp=0,qq=0;
        for(int i=q[0].L;i<=q[0].R;i++) { g[a[i]]++; op2(i); }
        Ans[q[0].id]=qq-pp;

        int L=q[0].L,R=q[0].R;
        for(int i=1;i<n-1;i++)
        {
            while(L<q[i].L) { g[a[L]]--; op1(L); L++; }
            while(L>q[i].L) { L--; g[a[L]]++; op2(L); }
            while(R>q[i].R) { g[a[R]]--; op1(R); R--; }
            while(R<q[i].R) { R++; g[a[R]]++; op2(R); }
            Ans[q[i].id]=qq-pp;
        }
        for(int i=0;i<n-1;i++) printf("%d\n",Ans[i]);
    }
    return 0;
}
时间: 2024-10-10 11:12:38

CSU 1811 Tree Intersection的相关文章

CSU 1811 Tree Intersection(平衡树的子树合并)

题意:对于每一条边,去掉一条边后,生成两颗树,问这两颗树的交集大小. 分析:1.O(n^2)算法:每次都用O(n)的时间去合并两个区间,因此可以从合并这个地方去优化.         2.可以这么考虑,在遍历树的时候计算去掉的两树的交集,那么每次我们只要把当前点的所有子树合并便可得到当前点的父边的解.         3.具体实现细节代码见. 复杂度是O(nlog^2n),具体可以参考大白p234那一道题. 吐槽:手挫,思路一早就有了,就代码一直写得-. /*******************

csu oj 1811: Tree Intersection (启发式合并)

题目链接:http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1811 给你一棵树,每个节点有一个颜色.问删除一条边形成两棵子树,两棵子树有多少种颜色是有相同的. 启发式合并,小的合并到大的中.类似的题目有http://codeforces.com/contest/600/problem/E 1 //#pragma comment(linker, "/STACK:102400000, 102400000") 2 #include <a

2016湖南省赛 I Tree Intersection(线段树合并,树链剖分)

2016湖南省赛 I Tree Intersection(线段树合并,树链剖分) 传送门:https://ac.nowcoder.com/acm/contest/1112/I 题意: 给你一个n个结点的树,树上每个节点有自己的颜色 问你删除第i条边后形成的两颗子树有多少个相同的颜色 题解: 树链剖分写法: 对于每一种颜色来说,如果这个颜色是在单独的一颗子树中,那么就不会对其他的边产生贡献,所以我们单独对每一种颜色对边的贡献讨论,如果这个颜色只有一个,那么就不会产生贡献,否则,他就可以在两个相同颜

CSU 1663: Tree(树链剖分)

1663: Tree Time Limit: 5 Sec  Memory Limit: 128 MB Submit: 26  Solved: 11 [Submit][Status][Web Board] Description CSU has a lot of trees. But there is a tree which is different from the others. This one is made of weighted edges and I have three kind

CSU1811: Tree Intersection

题目大意 给一棵点带颜色的树,问依次删掉每条边后得到的两棵子树出现的颜色并集的大小. 简要题解 注意这么一个性质,一种颜色在两棵树中出现,则在一棵树中出现,且出现次数小于总的出现次数. 然后需要这么一个数据结构,能维护子树内出现的颜色和对应的出现次数. 用数组启发式合并或者线段树合并即可. 之前写了一发,拍了好久没问题,交上去就RE,好气. 今天重新写了一发,写完直接AC,神清气爽hhh 1 #include <bits/stdc++.h> 2 using namespace std; 3 n

2016湖南省赛 [Cloned]

A.2016 给出正整数 n 和 m,统计满足以下条件的正整数对 (a,b) 的数量: 1. 1≤a≤n,1≤b≤m; 2. a×b 是 2016 的倍数. Input 输入包含不超过 30 组数据. 每组数据包含两个整数 n,m (1≤n,m≤10 9). Output对于每组数据,输出一个整数表示满足条件的数量.Sample Input 32 63 2016 2016 1000000000 1000000000 Sample Output 1 30576 7523146895502644 代

CSU 1414: Query on a Tree

预处理每个结点的子结点的个数sons , 则对x的询问可由sons[x]- sigma( sons[v] ) (v是到x距离为d的点)得到 怎么快速的找到这些v呢? 注意到距离x为d的点肯定在树的同一层.... 可以对树进行dfs时记录每个结点时间戳的同时把每一层的结点保存下来,然后对每一层维护一个前缀和 如果v是x下面子结点那么v的时间戳肯定在x的范围内,这样就可以二分確定出前缀和的范围了..... 1414: Query on a Tree Time Limit: 3 Sec  Memory

【线段树】CSU 1414 Query on a Tree

点击打开链接 线段树新功能get,太神奇了啊@[email protected] 先遍历下树,时间戳记录下前后时间 子节点的前后时间都会在父节点的前后时间范围内 用线段树维护区间内深度最大和深度最小 #include <cstdio> #include <cstring> #include <cstdlib> #include <string> #include <iostream> #include <algorithm> #inc

Hierarchical Tree Traversal in Graphics Pipeline Stages

BACKGROUND Many algorithms on a graphics processing unit (GPU) may benefit from doing a query in a hierarchical tree structure (including quad-trees, oct-trees, kd-trees, R-trees, and so forth). However, these trees can be very deep, whereby traversi