SPOJ.COT2 Count on a tree II(树上莫队)

题目链接(同上一题苹果树)

为什么第10个点T了一晚上。。
下面那个却AC了?跑的也不慢。

TLE:

/*
在DFS序做莫队
当一个点不是另一个点的LCA时,需要加上它们LCA的贡献
*/
#include <cmath>
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
//#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
const int N=5e4+5,M=1e5+5,MAXIN=2e6;

int n,m,size,Enum,H[N],nxt[N<<1],to[N<<1],dep[N],fa[N],top[N],son[N],sz[N];
int Now,Ans[M],seq[N<<1],in[N],out[N],id,A[N],ref[N],cnt,tm[N];
//char IN[MAXIN],*SS=IN,*TT=IN;
bool vis[N];
struct Ques
{
    int l,r,lca,id;
    bool operator <(const Ques &a)const{
        return l/size<a.l/size?r<a.r:l/size<a.l/size;
    }
}q[M];

//inline int read()
//{
//  int now=0,f=1;register char c=gc();
//  for(;!isdigit(c);c=gc()) if(c=='-') f=-1;
//  for(;isdigit(c);now=now*10+c-'0',c=gc());
//  return now*f;
//}
inline void AddEdge(int u,int v)
{
    to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
    to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
}
int Find(int x)
{
    int l=1,r=cnt,mid;
    while(l<r)
        if(ref[mid=l+r>>1]>=x) r=mid;
        else l=mid+1;
    return l;
}
void Discrete()
{
//  for(int i=1; i<=n; ++i) A[i]=ref[i]=read();
    for(int i=1; i<=n; ++i) scanf("%d",&A[i]),ref[i]=A[i];
    std::sort(ref+1,ref+1+n);
    cnt=1;
    for(int i=2; i<=n; ++i)
        if(ref[i]!=ref[i-1]) ref[++cnt]=ref[i];
    for(int i=1; i<=n; ++i) A[i]=Find(A[i]);
}
void Pre_DFS(int x)
{
    in[x]=++id, seq[id]=x, sz[x]=1;
    int mx=0;
    for(int v,i=H[x]; i; i=nxt[i])
        if((v=to[i])!=fa[x])
        {
            fa[v]=x, dep[v]=dep[x]+1, Pre_DFS(v), sz[x]+=sz[v];
            if(sz[v]>mx) mx=sz[v],son[x]=v;
        }
    out[x]=++id, seq[id]=x;
}
void DFS2(int x,int tp)
{
    top[x]=tp;
    if(son[x])
    {
        DFS2(son[x],tp);
        for(int i=H[x]; i; i=nxt[i])
            if(to[i]!=fa[x]&&to[i]!=son[x])
                DFS2(to[i],to[i]);
    }
}
int Query_LCA(int x,int y)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]]) std::swap(x,y);
        x=fa[top[x]];
    }
    return dep[x]>dep[y]?y:x;
}
void Calc(int p)
{
    if(vis[p])
        if(!--tm[A[p]]) --Now;
        else ;
    else if(++tm[A[p]]==1) ++Now;
    vis[p]^=1;
}

int main()
{
//  n=read(),m=read();
    scanf("%d%d",&n,&m);
    Discrete();
//  for(int u,v,i=1; i<n; ++i) u=read(),v=read(),AddEdge(u,v);
    for(int u,v,i=1; i<n; ++i) scanf("%d%d",&u,&v),AddEdge(u,v);
    Pre_DFS(1), DFS2(1,1);
    for(int u,v,w,i=1; i<=m; ++i)
    {
//      u=read(),v=read(),w=Query_LCA(u,v);
        scanf("%d%d",&u,&v),w=Query_LCA(u,v);
        q[i].id=i;//这要在continue前面。。
        if(u==v) {q[i].lca=-1; continue;}
        if(in[u]>in[v]) std::swap(u,v);
        if(w==u) q[i].l=in[w],q[i].r=in[v],q[i].lca=0;//这部分的LCA不能和u=v时用一样的!
//      else if(w==v) q[i].r=in[w],q[i].r=in[u],q[i].lca=0;
        else q[i].l=out[u],q[i].r=in[v],q[i].lca=in[w];//in[w] not w!
    }
    size=sqrt(id), std::sort(q+1,q+1+m);
    for(int l=1,r=0,i=1; i<=m; ++i)
        if(q[i].lca==-1) Ans[q[i].id]=1;
        else
        {
            while(l<q[i].l) Calc(seq[l++]);
            while(l>q[i].l) Calc(seq[--l]);
            while(r<q[i].r) Calc(seq[++r]);
            while(r>q[i].r) Calc(seq[r--]);
            if(q[i].lca) Calc(seq[q[i].lca]);//seq[in[lca]] not in[lca]!
            Ans[q[i].id]=Now;
            if(q[i].lca) Calc(seq[q[i].lca]);
        }
    for(int i=1; i<=m; ++i) printf("%d\n",Ans[i]);

    return 0;
}

AC: 2.21s 8.5M

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;

typedef long long ll;
const int N = 50010;
struct edge
{
    int to, next;
}g[N*2];
int n, m, unit;
struct node
{
    int l, r, anc, id;
    bool operator <(const node &b)const {return l/unit != b.l/unit ? l/unit < b.l/unit : r < b.r;}
}q[N*2];
int arr[N], ref[N];
int cnt, head[N];
int dep[N];
int in[N], out[N], seq[N*2], num;
int res[N*2], tmp, ver[N];
bool vis[N];
void add_edge(int v, int u)
{
    g[cnt].to = u, g[cnt].next = head[v], head[v] = cnt++;
}
void dfs(int v)
{
    in[v] = ++num;
    seq[num] = v;
    for(int i = head[v]; i != -1; i = g[i].next)
    {
        int u = g[i].to;
        if(! dep[u])
            dep[u] = dep[v] + 1, dfs(u);
    }
    out[v] = ++num;
    seq[num] = v;
}
namespace Subd
{
    int dep[N],fa[N],top[N],son[N],sz[N];
    void Pre_DFS(int x)
    {
        sz[x]=1;
        int mx=0;
        for(int v,i = head[x]; i != -1; i = g[i].next)
            if((v=g[i].to)!=fa[x])
            {
                fa[v]=x, dep[v]=dep[x]+1, Pre_DFS(v), sz[x]+=sz[v];
                if(sz[v]>mx) mx=sz[v],son[x]=v;
            }
    }
    void DFS2(int x,int tp)
    {
        top[x]=tp;
        if(son[x])
        {
            DFS2(son[x],tp);
            for(int v,i = head[x]; i != -1; i = g[i].next)
                if((v=g[i].to)!=fa[x]&&v!=son[x])
                    DFS2(v,v);
        }
    }
    int Query_LCA(int x,int y)
    {
        while(top[x]!=top[y])
        {
            if(dep[top[x]]<dep[top[y]]) std::swap(x,y);
            x=fa[top[x]];
        }
        return dep[x]>dep[y]?y:x;
    }
}
void Calc(int p)
{
    if(vis[p])
        if(!--ver[arr[p]]) --tmp;
        else ;
    else if(++ver[arr[p]]==1) ++tmp;
    vis[p]^=1;
}
int tot;
int Find(int x)
{
    int l=1,r=tot,mid;
    while(l<r)
        if(ref[mid=l+r>>1]>=x) r=mid;
        else l=mid+1;
    return l;
}
void Discrete()
{
    for(int i=1; i<=n; ++i) ref[i]=arr[i];
    sort(ref+1,ref+1+n);
    tot=1;
    for(int i=2; i<=n; ++i)
        if(ref[i]!=ref[i-1]) ref[++tot]=ref[i];
    for(int i=1; i<=n; ++i) arr[i]=Find(arr[i]);
}
//bool cmp(node a, node b){return a.l/unit != b.l/unit ? a.l/unit < b.l/unit : a.r < b.r;}

void solve()
{
    int a, b;
    for(int i = 1; i <= n; i++)
        scanf("%d", &arr[i]);
    Discrete();
    cnt = 0;
    memset(head, -1, sizeof head);
    for(int i = 1; i <= n - 1; i++) //存树
        scanf("%d%d", &a, &b), add_edge(a, b), add_edge(b, a);
    memset(dep, 0, sizeof dep);
    dep[1] = 1;
    num = 0;
    dfs(1);
    Subd::Pre_DFS(1), Subd::DFS2(1,1);
    for(int u,v,i = 1; i <= m; i++)
    {
        scanf("%d%d", &u, &v);
        q[i].id = i;
        if(u == v) q[i].anc = -1; //特殊标记,此时公共祖先应该为1
        else
        {
            int w=Subd::Query_LCA(u,v);
            if(in[u]>in[v]) swap(u,v);
            if(w==u) q[i].l=in[w],q[i].r=in[v],q[i].anc=0;
            else q[i].l=out[u],q[i].r=in[v],q[i].anc=w;
        }
    }
    unit = sqrt(num);
    sort(q + 1, q + 1 + m);//分块排序
    int l = 1, r = 0;
    tmp = 0;
    for(int i = 1; i <= m; i++)
        if(q[i].anc==-1) res[q[i].id]=1;
        else
        {
            while(l<q[i].l) Calc(seq[l++]);
            while(l>q[i].l) Calc(seq[--l]);
            while(r<q[i].r) Calc(seq[++r]);
            while(r>q[i].r) Calc(seq[r--]);
            if(q[i].anc>0) Calc(seq[in[q[i].anc]]);
            res[q[i].id]=tmp;
            if(q[i].anc>0) Calc(seq[in[q[i].anc]]);
        }
    for(int i = 1; i <= m; i++) printf("%d\n", res[i]);
}
int main()
{
    scanf("%d%d", &n, &m);
    solve();
    return 0;
}

原文地址:https://www.cnblogs.com/SovietPower/p/8476895.html

时间: 2024-11-07 01:58:30

SPOJ.COT2 Count on a tree II(树上莫队)的相关文章

SP10707 COT2 - Count on a tree II (树上莫队)

参考博客 对于树上的路径询问问题 O(1)的时间加入或删除一个点的贡献 -> \(O(n\sqrt n)\)的复杂度求出所有询问的答案 对树上的结点进行分块,离线询问后排序,顺序遍历暴力转移路径(转移时加入或删除路径上的点的贡献即可). 关于转移路径:首先定义路径:设\(T_u\)为\(u\) 到根的路径上边的集合,那么\(u\)到\(v\) 的路径上的边的集合就是\(T_u \triangle T_v\) (\(\triangle\) 是对称差).要从\(u\rightarrow v\) 转移

spoj COT2 - Count on a tree II

COT2 - Count on a tree II http://www.spoj.com/problems/COT2/ #tree You are given a tree with N nodes. The tree nodes are numbered from 1 to N. Each node has an integer weight. We will ask you to perform the following operation: u v : ask for how many

SPOJ COT2 Count on a tree II (树上莫队,倍增算法求LCA)

题意:给一个树图,每个点的点权(比如颜色编号),m个询问,每个询问是一个区间[a,b],图中两点之间唯一路径上有多少个不同点权(即多少种颜色).n<40000,m<100000. 思路:无意中看到树上莫队,只是拿来练练,没有想到这题的难点不在于树上莫队,而是判断LCA是否在两点之间的路径上的问题.耗时1天. 树上莫队的搞法就是: (1)DFS一次,对树进行分块,分成sqrt(n)块,每个点属于一个块.并记录每个点的DFS序. (2)将m个询问区间用所属块号作为第一关键字,DFS序作为第二关键字

SPOJ COT2 Count on a tree II(树上莫队)

题目链接:http://www.spoj.com/problems/COT2/ You are given a tree with N nodes.The tree nodes are numbered from 1 to N.Each node has an integer weight. We will ask you to perfrom the following operation: u v : ask for how many different integers that repr

SPOJ:COT2 Count on a tree II

题意 给定一个n个节点的树,每个节点表示一个整数,问u到v的路径上有多少个不同的整数. n=40000,m=100000 Sol 树上莫队模板题 # include <bits/stdc++.h> # define RG register # define IL inline # define Fill(a, b) memset(a, b, sizeof(a)) using namespace std; const int _(1e5 + 5); typedef long long ll; I

BZOJ 2589 Spoj 10707 Count on a tree II 强制在线莫队算法(TLE)

题目大意:给定一棵树,每个节点有一个颜色,多次询问某条路径上颜色数量,强制在线 正解是块状数组,强制在线莫队会TLE到死,想AC这道题的不用看了 如果朴素的跑树上莫队其实并不难- - 但是强制在线 因此我们可以考虑强制在线莫队算法 将树分成O(n^1/3)块,每块大小O(n^2/3) 记录每两块之间的答案.每种颜色的出现次数和哪些点被记录到了答案中 每次查询先找到两端点所在块的端点的答案,然后暴力用莫队转移即可 空间复杂度O(n^1/3)*O(n^1/3)*O(n)=O(n^5/3) 预处理时间

【SPOJ10707】COT2 - Count on a tree II

题目大意:给定一棵 N 个节点的无根树,每个节点有一个颜色.现有 M 个询问,每次询问一条树链上的不同颜色数. 题解:学会了树上莫队. 树上莫队是将节点按照欧拉序进行排序,将树上问题转化成序列上的问题进行求解的算法.需要分两种情况进行讨论,第一种情况是对于询问 x,y 来说,x 为 y 的祖先,则询问的区间为 \(st[x],st[y]\),第二种情况是 x 与 y 处在两个不同的子树内,这时发现 \(lca(x,y)\) 的欧拉序并不在 [ed[x], st[y]] 内,因此需要额外考虑 lc

SPOJ10707 COT2 - Count on a tree II 【树上莫队】

题目分析: 考虑欧拉序,这里的欧拉序与ETT欧拉序的定义相同而与倍增LCA不同.然后不妨对于询问$u$与$v$让$dfsin[u] \leq dfsin[v]$,这样对于u和v不在一条路径上,它们可以改成询问$dfsin[u]$到$dfsin[v]$.否则改成$dfsout[u]$到$dfsin[v]$,并加上LCA上的影响,如果在询问过程中没有加入就加入,否则删除. 代码: 1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const

bzoj2589: Spoj 10707 Count on a tree II

Description 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v),你需要回答u xor lastans和v这两个节点间有多少种不同的点权.其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文. Input 第一行两个整数N,M. 第二行有N个整数,其中第i个整数表示点i的权值. 后面N-1行每行两个整数(x,y),表示点x到点y有一条边. 最后M行每行两个整数(u,v),表示一组询问. 数据范围是N<=40000 M<=100000 点权在int范围内