JZOJ 5438. 【NOIP2017提高A组集训10.31】Tree

本文介绍了一个基于图论的算法优化方案,通过使用深度优先搜索(DFS)改进算法效率,并利用线段树等数据结构实现高效的节点颜色反转操作。文章详细讨论了如何避免递归过程中的栈溢出问题,并建议在特定情况下采用广度优先搜索(BFS)或其他替代方法。

题目

这里写图片描述
样例输入
这里写图片描述

题解

由于确定了一条边的父子关系,所以当父亲和儿子的颜色不一致时,直接对儿子进行反色操作。因为父亲进步进行反色操作,父与子的颜色还是不一致。
然而我蠢。
我维护了基于dfs的前缀和,本来想画个图l向r+1连条边,结果没有进展。
因为dfs序前面的会影响后面,后面不会影响前面,所以我还打了个线段树,带了个log。
代码长就不说了,递归还爆栈。
另外,看清点数。对于点数n≤5∗105,不要dfs,用bfs/人工栈!!!!

代码(较蠢)

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define N 500010
#define P(a) putchar(a)
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
struct sgm{
    int sum,lazy;
};sgm tr[N*20];
struct note{
    int to,next;
};note edge[N*2];
int val[N],siz[N],ans[N],fa[N];
int dfn[N],tar[N],head[N],head1[N];
int i,j,k,l,n,m,T,temp;
int u,v,tot;
int st[N];
bool bz[N];
int read(){
    int res=0,fh=1;char ch;
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')fh=-1,ch=getchar();
    while(ch>='0'&&ch<='9')res=res*10+ch-'0',ch=getchar();
    return res*fh;
}
void write(int x){
    if(x>9)write(x/10);
    P(x%10+'0');
}
void lb(int x,int y){
    edge[++tot].to=y;
    edge[tot].next=head[x];
    head[x]=tot;
}
void update(int ps){
    tr[ps].sum=tr[ps<<1].sum+tr[(ps<<1)+1].sum;
}
void downld(int ps){
    if(tr[ps].lazy!=0){
        tr[ps<<1].lazy+=tr[ps].lazy;
        tr[(ps<<1)+1].lazy+=tr[ps].lazy;
        tr[ps<<1].sum+=tr[ps].lazy;
        tr[(ps<<1)+1].sum+=tr[ps].lazy;
        tr[ps].lazy=0;
    } 
}
void dg(){
    st[st[0]=1]=1;memcpy(head1,head,sizeof(head1));
    bool p;
    int x,i;
    while(st[0]){
        x=st[st[0]];
        if(!bz[x]){
            dfn[x]=++T;tar[T]=x;siz[x]=1;
            bz[x]=1;
        }
        p=0;
        for(i=head1[x];i;i=edge[i].next)
        if(fa[x]!=edge[i].to){
            fa[edge[i].to]=x;
            st[++st[0]]=edge[i].to;
            p=1;
            head1[x]=edge[i].next;
            break;
        }
        if(!p){
            if(st[0]>1)siz[st[st[0]-1]]+=siz[x];
            st[0]--;
        }
    }
}
void build(int ps,int l,int r){
    if(l==r){
        tr[ps].sum=val[tar[l]];
        return;
    }
    int wz=(l+r)>>1;
    build(ps<<1,l,wz);
    build((ps<<1)+1,wz+1,r);
    update(ps);
}
int find(int ps,int l,int r,int x){
    if(l==r)return tr[ps].sum;
    downld(ps);
    int wz=(l+r)>>1;
    if(x<=wz)return find(ps<<1,l,wz,x);
        else return find((ps<<1)+1,wz+1,r,x);
    update(ps);
}
void change(int ps,int l,int r,int x,int y,int z){
    if(l==x && r==y){
        tr[ps].sum+=z;
        tr[ps].lazy+=z;
        return;
    }
    downld(ps);
    int wz=(l+r)>>1;
    if(y<=wz)change(ps<<1,l,wz,x,y,z);else
    if(x>wz)change((ps<<1)+1,wz+1,r,x,y,z);else{
        change(ps<<1,l,wz,x,wz,z);
        change((ps<<1)+1,wz+1,r,wz+1,y,z);
    }
    update(ps);
}
int main(){
    n=read();
    fo(i,1,n)val[i]=read();
    fo(i,1,n-1){
        u=read(),v=read();
        lb(u,v);lb(v,u);
    }
    dg();
    build(1,1,n);
    fo(i,1,n){
        temp=find(1,1,n,i);
        if(temp&1){
            ans[++ans[0]]=tar[i];
            change(1,1,n,i,i+siz[tar[i]]-1,1);
        }
    }
    sort(ans+1,ans+ans[0]+1);
    fo(i,1,ans[0]){
        write(ans[i]);
        if(i<ans[0])P(' ');
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值