首页 > 其他分享 >P5311 Ynoi2011 成都七中

P5311 Ynoi2011 成都七中

时间:2024-04-13 16:45:39浏览次数:14  
标签:七中 int Ynoi2011 mx book edge siz P5311 节点

P5311 Ynoi2011 成都七中

点分树好题,太妙了。

思路

看到树和连通块,考虑点分树。

但是从这里发现原树和点分树的联系实在太小,貌似不可做。

可以发现对于一个询问,一个点如果和 \(x\) 在一个连通块内,那么这个点到 \(x\) 的最大最小节点编号肯定都在 \([l,r]\) 这个范围内。

可以证明,对于一个连通块,肯定存在一个在 \(x\) 的连通块内的节点 \(u\),节点 \(u\) 在点分树上最浅,且节点 \(u\) 在点分树上的子树包含了整个连通块。

证明如下:

证明一:在原树中删除点分树上的一个点,会把原树分为若干个连通块,在构建点分树的过程中 \(x\) 的连通块内,最先被选中作为点分树上的一个节点为 \(u\) ,根据构建规则,剩下的点会成为 \(u\) 的子树内的节点,满足了 \(u\) 最浅和全覆盖。

证明二:如果点分树上一个节点 \(v\) 不是在连通块内,那么 \(x\) 连通块内的所有节点肯定都在该节点的同一个儿子的子树中。因为如果在不同儿子的子树中,这样节点 \(v\) 肯定也会在 \(x\) 的连通块中,与题设不符。顺着这个儿子走,直到走到一个在连通块内的点即可。

想要找到点 \(u\) 只需要依次遍历点分树上 \(x\) 最浅的祖先,选择满足原树上到 \(x\) 的最大最小节点编号在 \([l,r]\) 内的最浅的祖先。我们将这个选择的点定义为关键点。

每一个询问都对应了一个关键点,我们只需要查找点分树上关键点所在子树内,原树上到关键点最大最小节点编号在 \([l,r]\) 内的节点即可。(原树上关键点到 \(x\) 最大最小节点编号在 \([l,r]\) 内,可以从子树内的这个点到关键点,再到 \(x\) ,原树上最大最小编号也肯定在 \([l,r]\) 内)

如果每个询问做一次,效率甚至不如暴力,但我们可以把多个询问合在同一个点上做。

对于一个点分树上的点 \(u\),我们把其子树内的点到 \(u\) 的原树上最大最小编号计为 \((mn,mx)\),关键点为 \(u\) 询问存为 \((l,r)\)。

把询问和点混在一起按照第一维降序排序,然后每一种颜色记录最小的 \(mx\),遇到一个查询直接查询有多少颜色的 \(mx\) 小于 \(r\) 即可,这里可以用树状数组维护。

这里先加入树状数组的点满足了 \(l\leq mn\),查询时又满足了 \(mx\leq r\)。

时间复杂度:

树分治的子树内节点排序:\(O(n\log^2 n)\)。

树分治加树状数组:\(O(n\log^2 n)\)。

总时间复杂度:\(n\log^2 n\)。

CODE

#include<bits/stdc++.h>
using namespace std;

const int maxn=1e5+5;

struct Edge
{
    int tot;
    int head[maxn];
    struct edgenode{int to,nxt;}edge[maxn*2];
    inline void add(int x,int y)
    {
        tot++;
        edge[tot].to=y;
        edge[tot].nxt=head[x];
        head[x]=tot;
    }
}T;
struct node{int x,y,c,tp;};
struct treearray
{
    int tree[maxn];
    inline int lowbit(int x){return x&(-x);}
    inline void updata(int x,int y){for(;x<=maxn-5;x+=lowbit(x)) tree[x]+=y;}
    inline int getsum(int x){int sum=0;for(;x;x-=lowbit(x)) sum+=tree[x];return sum;}
    inline void clr(int x){for(;x<=maxn-5;x+=lowbit(x)) tree[x]=0;}
}S;
struct fanode{int x,y,f;};//加入祖先时,记录到祖先的最大最小节点编号

inline bool cmp(node a,node b){return a.x>b.x||(a.x==b.x&&a.tp<b.tp);}

int n,m,rt;
int c[maxn],siz[maxn],mv[maxn],ans[maxn];
int mx[maxn],mn[maxn];

vector<int>s[maxn];
vector<fanode>fa[maxn];
vector<node>t[maxn];

bool book[maxn],cut[maxn];

inline int dfs_siz(int u)
{
    book[u]=true;siz[u]=1;
    for(int i=T.head[u];i;i=T.edge[i].nxt)
    {
        int v=T.edge[i].to;
        if(!book[v]&&!cut[v]) siz[u]+=dfs_siz(v);
    }
    book[u]=false;return siz[u];
}
inline int dfs_rt(int u,const int tot)
{
    book[u]=true;int ret=u;
    for(int i=T.head[u];i;i=T.edge[i].nxt)
    {
        int v=T.edge[i].to;
        if(!book[v]&&!cut[v]&&siz[v]*2>=tot){ret=dfs_rt(v,tot);break;}
    }
    book[u]=false;return ret;
}
inline void dfs2(int u,int g,int mn,int mx)
{
    mx=max(mx,u),mn=min(mn,u);
    t[g].push_back({mn,mx,c[u],0});//加入节点
    book[u]=true;fa[u].push_back({mn,mx,g});
    for(int i=T.head[u];i;i=T.edge[i].nxt)
    {
        int v=T.edge[i].to;
        if(!book[v]&&!cut[v]) dfs2(v,g,mn,mx);
    }
    book[u]=false;siz[u]=1;
}
inline void solve(int u,int f)//建树
{
    dfs_siz(u);int g=dfs_rt(u,siz[u]);cut[g]=true;
    s[f].push_back(g),fa[g].push_back({g,g,g});
    t[g].push_back({g,g,c[g],0});
    for(int i=T.head[g];i;i=T.edge[i].nxt)
    {
        int v=T.edge[i].to;
        if(!cut[v]){dfs2(v,g,g,g);solve(v,g);}
    }
    rt=g;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&c[i]);
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        T.add(x,y),T.add(y,x);
    }
    solve(1,0);
    for(int i=1;i<=m;i++)
    {
        int l,r,x;
        scanf("%d%d%d",&l,&r,&x);
        for(auto j:fa[x])
            if(l<=j.x&&j.y<=r){t[j.f].push_back({l,r,i,1});break;}
    }
    memset(mv,0x7f,sizeof(mv));
    for(int i=1;i<=n;i++)
    {
        sort(t[i].begin(),t[i].end(),cmp);
        for(auto j:t[i])
            if(j.tp) ans[j.c]=S.getsum(j.y);
            else if(j.y<mv[j.c]){if(mv[j.c]<1e9)S.updata(mv[j.c],-1);S.updata(mv[j.c]=j.y,1);}
        for(auto j:t[i]) if(!j.tp) S.clr(mv[j.c]),mv[j.c]=1e9;
    }
    for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
}

标签:七中,int,Ynoi2011,mx,book,edge,siz,P5311,节点
From: https://www.cnblogs.com/binbinbjl/p/18133038

相关文章

  • 七中英才集训游记
    Day1noip模拟赛。T1:给出一个n,将其分解为\(\frac{a!}{b!}\)的方案。由于阶乘的数值巨大,13的阶乘就已经爆int了,所以二分+枚举解决。T2:蜜汁构造题,思考10分钟后直接跳过。T3:数据结构题,考场想到86分的\(O(nlog^2)\)超劣解法,需要在线段树的每一个节点上开\(vector\),查找时......
  • 2024 1/2 成都七中训练
    道路(road)竞赛图三元环计数,答案为\(C(n,3)-\sum_{i=1}^{n}C(du(i),2)\)。扫描线维护出度就好了。图(graph)考虑最优解是一个森林。我们想到转到网络流维护。尝试将图上点随机染色,白点连\(S\),黑点连\(T\),之间的边互连,这样网络流就能找到最优解。正确率为\(1-(1-\frac{1}{......
  • 2024 成都七中集训的 50 件小事
    https://weibo.com/ttarticle/p/show?id=2309404965130831265859&luicode=10000011&lfid=1005056790194958在想到写游记的时候一下子就想到了这个,虽然无关,但是还是很遗憾2023世界杯的亚军。2024成都七中集训的50件小事学校里的超市没有可乐卖,我觉得应该不止我一个人想喝......
  • #根号分治,分块#洛谷 5309 [Ynoi2011] 初始化
    题目传送门分析如果\(x\)比较大那么可以暴力修改,\(x\)比较小的话可以用数组打标记查询的时候对于暴力修改的部分可以分块,暴力修改的同时需要给块打标记如果\(x\)比较小的情况,一段区间相当于是中间很多段周期加上前后缀(当然可以直接区间减但是我被卡常了)我调的块长是160......
  • 洛谷 P5311 [Ynoi2011] 成都七中
    洛谷传送门转化一下题意,变成求\(x\)在只经过编号\(\in[l,r]\)的点,能走到多少种颜色。考虑建出点分树。一个结论是原树上的一个连通块,一定存在一个点,使得它在点分树上的子树完全包含这个连通块的所有点。证明考虑点分治的过程,一个连通块如果没被其中一个点剖开就一定在同一......
  • P5314 [Ynoi2011] ODT
    好题,牛牛的一个套路。先树剖一下,我们可以很简单的用树状数组维护每个点的真实值。对于每个点只维护所有轻儿子的信息,对于每次询问的时候暴力加入当前点,重儿子以及父亲的信息,查询第\(k\)大,再删除信息即可。考虑链修改的影响。因为只维护的是轻儿子的信息,那么只有链上的所有轻......
  • P5311 [Ynoi2011] 成都七中
    我永远喜欢数据结构。题目传送门给出\(n\)个点的树,点有颜色\(a_i\)。有\(q\)次询问,每次询问给出\(l,r,x\),求保留\([l,r]\)范围内的节点时,\(x\)所在联通块中有多少种本质不同的颜色。询问之间相互独立。不保留一个点的定义是,将这个点以及与其相邻的边全部删除。......
  • P5309 [Ynoi2011] 初始化
    题目传送门本来不想写这道\(shabi\)卡肠题的,但还是写了。分块+根号分治。考虑对\(x\)的大小分类讨论:若\(x>=\sqrt{n}\),很明显最多只会加\(\sqrt{n}\)次,暴力加即可,用分块维护每个块内的\(sum\),查询就直接散块加上整块即可。若\(x<\sqrt{n}\),考虑累加\(x,y\)相同的......
  • 「Ynoi2011」成都七中
    「Ynoi2011」成都七中题意:询问\(([l,r],x)\),表示将树中编号在\([l,r]\)内的所有节点保留,求\(x\)所在连通块中颜色种类数可以转化为从\(x\)出发且只经过节点范围在\([l,r]\)的路径上的颜色种类数,是路径问题且多次询问,所以可以考虑点分树但是可以发现,一个节点的答案可......
  • 成都七中 解题报告
    点分树有这么一个性质:你总能找到一个点,使得原树中这个点所在的连通块在这个点的子树内(如果整棵树没有被破坏,这个点就是根节点)。这个结论过于显然,证明只有一句话:点分树上的......