欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

三校联考(二) tree

程序员文章站 2022-06-02 12:39:00
...

三句话题意:给你一棵树,每个点有它的初始权值,有三种操作(o):
操作1(o=0):询问以x为根的子树中有多少节点的值大于y。
操作2(o=1):将编号为x的节点的值改成y。
操作3(o=2):添加一个点,其编号为树中原有节点数+1,它的父亲为x,权值为y。




给大家看一下正确的题解和正确的算法。

Tree解题报告

对于30%的数据
不多说了,直接暴力,只是你愿不愿意打的问题

对于60%的数据
简单粗暴的办法,对于树上的每个点建立一个数组以保存状态,即这个点用来保存这个点和这个点所有孩子的权值。
对于数据,n*max(权值)=10^9过大,所以还要打个离散化权值(即离线做)。
由于数据比较水,所以可能会有些奇奇怪怪的方法卡了高分。

对于100%的数据
核心思想:树上分块(据说某大神会用可修改的dfs序来分块,有兴趣的同学可以试试)
首先设定一个块大小的界限
对于点修改操作(即op=1),修改这个点所属块的状态
对于点插入操作(即op=2),判断此点的父节点所在块是否满了:若满了则新建一个块,并将两个块(新建块和父节点所属块)建立连边;若没有满则将此点添加入块中,并修改块的状态。
对于点查询操作(即op=3),当查询某个点时,若查询点是其所属块的根节点(插入操作保证每个块有且仅有一个根),则直接对块查询,此后一直进行块查询(通过块的连边),否则继续对孩子点查询,直到查询到块为止。
这样做可以保证对于任何操作,平均的时间复杂度为 (在块更新的时候有个暴力重构也许要再乘一个lg(n)),然后就稳稳的卡进了时限(╹▽╹)。

然后是标程:

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define Maxn 300010
#define limit 200
using namespace std;
int n,m,u,v,last,op;char ch;
int sum1,sum2,sum3,ad1,ad2;
int w[Maxn<<1],father[Maxn<<1],rec[Maxn],nxt1[Maxn<<1],pre1[Maxn<<1],nxt2[Maxn<<1],pre2[Maxn<<1],end1[Maxn],end2[Maxn];
struct Node
{
    int length,a[limit];
}B[40000];
void add1(int x,int y)
{
    nxt1[++ad1]=y;pre1[ad1]=end1[x];end1[x]=ad1;
}
void add2(int x,int y)
{
    nxt2[++ad2]=y;pre2[ad2]=end2[x];end2[x]=ad2;
}
inline void read(int&x)
{
    ch=getchar();x=0;
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
}
void build(int u)        //dfs加点,按操作op=2的方式处理孩子 
{
    for(int i=end1[u];i>0;i=pre1[i]) 
        if(father[u]!=nxt1[i])
        {
            int T;
            if(B[rec[u]].length<limit) 
            {
            T=rec[nxt1[i]]=rec[u];
            B[T].a[B[T].length++]=w[nxt1[i]];
            }
            else 
            {
            T=rec[nxt1[i]]=++sum2;
            B[sum2].a[B[sum2].length++]=w[nxt1[i]];
            add2(rec[u],T);
            }
        father[nxt1[i]]=u;sort(B[T].a,B[T].a+B[T].length);
        build(nxt1[i]);
        }
}
void update(int u,int v)        //当块内节点更新时, 暴力重构当前块 
{
    int T=rec[u];
    int k=lower_bound(B[T].a,B[T].a+B[T].length,w[u])-B[T].a;
    B[T].a[k]=v;w[u]=v;
    sort(B[T].a,B[T].a+B[T].length);
}
int bdfs(int u,int x)        //树上块查询 
{
    int sum=B[u].length-(upper_bound(B[u].a,B[u].a+B[u].length,x)-B[u].a);
    for(int i=end2[u];i>0;i=pre2[i])
        sum+=bdfs(nxt2[i], x);
    return sum;
}
int pdfs(int u, int x)        //树上点查询 
{
    int sum=0;
    if(w[u]>x) sum++;
    for(int i=end1[u];i>0;i=pre1[i]) 
        if(nxt1[i]!=father[u]) 
        { 
            if(rec[u]==rec[nxt1[i]]) sum+=pdfs(nxt1[i],x);
                else sum+=bdfs(rec[nxt1[i]],x);
        }
    return sum;
}
int main()
{
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    read(n);
    for(int i=1;i<n;i++) read(u),read(v),add1(u,v),add1(v,u);
    for(int i=1;i<=n;i++) read(w[i]);
    father[1]=0;
    B[++sum2].length=1;
    B[sum2].a[0]=w[1];
    rec[1]=sum2;
    build(1);
    read(m);
    while (m--)
    {
        read(op);read(u);read(v);

        if(op==0) last=pdfs(u,v),printf("%d\n",last);
        if(op==1) update(u,v);
        if(op==2)
        {
            w[++n]=v;
            add1(u,n);
            father[n]=u;
            int T=rec[u];
            if(B[T].length<limit) B[T].a[B[T].length++]=v,rec[n]=rec[u];
            else T=++sum2,B[T].a[B[T].length++]=v,add2(rec[u],sum2),rec[n]=sum2;   //添加新的块 
            sort(B[T].a,B[T].a+B[T].length);        //暴力构建状态 
        }
    }
 }


怎么样,是不是很难?

But

暴力的访问也就是3~4*10^8次,所以
三校联考(二) tree

暴力是必然的。

以下是满分代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=6e6+1000;
struct node{
    int v,nxt;
}edge[maxn<<1];
int head[maxn],tot=0;
int n,m;
int a[maxn],fa[maxn];
void add_edge(int x,int y){
    edge[tot].nxt=head[x];
    edge[tot].v=y;
    head[x]=tot++;
    return ;
}
void init(int u,int d){
    fa[u]=d;
    for(int i=head[u];i!=-1;i=edge[i].nxt){
        if(edge[i].v==d) continue;
        init(edge[i].v,u);
    }
    return ;
}
int query(int u,int y){
    int ret=a[u]>y;
    for(int i=head[u];i!=-1;i=edge[i].nxt){
        if(edge[i].v==fa[u]) continue;
            ret+=query(edge[i].v,y);
    }
    return ret;
}
int main()
{
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    memset(head,-1,sizeof(head));
    cin>>n;
    for(int i=1;i<n;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        add_edge(x,y);
        add_edge(y,x);
    }
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    init(1,-1);
    cin>>m;
    for(int i=1;i<=m;i++){
        int o,x,y;scanf("%d%d%d",&o,&x,&y);
        if(o==0){
            cout<<query(x,y)<<endl;
        }
        else if(o==1){
            a[x]=y;
        }
        else if(o==2){
            add_edge(x,++n);
            fa[n]=x;
            a[n]=y;
        }
    }
    return 0;
}
相关标签: 题解