JZOJ5463. 【NOIP2017提高A组冲刺11.8】证书

Description

Pulumi生活在P城的角落,而他的朋友们gjdy,oyski,tutuwai等等生活在P城的靠中心位置。
P城很大,但它拥有优秀的城市结构,同时P城重视文化教育的发展,P城共有n个学校,校与校之间共建立了n-1条交通线路,且两所学校之间存在唯一的连通路径。
P城常常举行各种类型的评比活动,为了节约资金,最终将给某一条路径上的所有学校颁发证书。为了便于描述我们记一次评比活动的结果为(ui,vi,zi)表示路径(ui,vi)上的所有学校获得一个类型为zi的证书。
一个学校若为Zmax类型的学校,则表示它在Zmax类型下的证书数量最多(如果有相同数量的类型,取类型标号最小一个)。
Pulumi收集了本年度所有的评比活动结果,共m次。他很感兴趣所有学校的类型,以了解他朋友们学校的状况,现在他忙于出题,把这个任务交给了你。

Input

第一行,两个整数n,m,如题中所述。
下接n-1行,每行两个整数u,v,表示标号u和v的学校之间有一条直接相连的路。
下接m行,每行三个整数u,v,z,表示一次结果为(u,v,z)的评比活动。

Output

共n行,第i行,一个整数zi,表示标号为i的学校类型为zi。

Sample Input

5 3
1 2
3 1
3 4
5 3
2 3 3
1 5 2
3 3 3

Sample Output

2
3
3
0
2

Data Constraint

对于30%的数据1<=N<=1000,1<=M<=1000
另外在30%的数据满足i-1与i之间有一条直接相连的路
对于100%的数据1<=N<=100000,0<=M<=100000,1<=zi<=10^9

题解

树上的路径修改?LCT?
虽然可以用LCT做,但是还有别的更好的方法。
对于一个点,
如何求出最小而且次数最多的?
可以用线段树,
然而每一个点都开一棵线段树?
空间估计不会允许。
可以用可持久化。

树上给一条路径加1,
就是在两个点+1,lca-1,lac的父亲-1。
类似的,先在这些对于的线段树里面对应的位置+1或者-1
然后线段树合并,
将全部儿子的线段树合并到这个点,

code

#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#define N 100003
#define M ((l+r)>>1)
#define ll long long
#define ls t[x].l
#define rs t[x].r
#define lt t[y].l
#define rt t[y].r
using namespace std;

char ch;
void read(int& n)
{
    n=0;
    for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
    for(;'0'<=ch && ch<='9';n=(n<<3)+(n<<1)+ch-48,ch=getchar());
}

struct node
{
    int x,y,z;
}a[N];

bool cmp(node a,node b)
{
    return a.z<b.z;
}

void write(int x)
{
    if(x>9)write(x/10);
    putchar(x%10+48);
}

struct tree{
    int l,r,mx,w,s;
}t[8000003];

int n,m,x,y,ans[N],w[N],ops,opx;
int nxt[N*2],to[N*2],b[N],tot;
int deep[N],f[18][N],L;

void ins(int x,int y)
{
    nxt[++tot]=b[x];
    to[tot]=y;
    b[x]=tot;
} 

int lca(int x,int y)
{
    if(deep[x]>deep[y])swap(x,y);
    for(int j=16;j>=0;j--)
        if(deep[x]<=deep[f[j][y]])y=f[j][y];
    if(x==y)return x;
    for(int j=16;j>=0;j--)
        if(f[j][x]!=f[j][y])x=f[j][x],y=f[j][y];
    return f[0][x];
}

void dfs(int x)
{
    for(int i=b[x];i;i=nxt[i])
        if(f[0][x]!=to[i])
        {
            f[0][to[i]]=x;
            deep[to[i]]=deep[x]+1;
            dfs(to[i]);
        }
}

void updata(int x)
{
    if(!ls)
    {
        t[x].mx=t[rs].mx;
        t[x].w=t[rs].w;
        return;
    }
    if(!rs)
    {
        t[x].mx=t[ls].mx;
        t[x].w=t[ls].w;
        return;
    }
    if(t[ls].mx>=t[rs].mx)
    {
        t[x].mx=t[ls].mx;
        t[x].w=t[ls].w;
        return;
    }
    else
    {
        t[x].mx=t[rs].mx;
        t[x].w=t[rs].w;
        return;
    }
}

void add(int x,int l,int r)
{
    if(l==r)
    {
        t[x].s+=opx;
        t[x].mx=t[x].s;
        t[x].w=l;
        return;
    }
    int m=M;
    if(ops<=m)
    {
        if(!ls)ls=++tot;
        add(ls,l,m);
    }
    else
    {
        if(!rs)rs=++tot;
        add(rs,m+1,r);
    }
    updata(x);
}

void hb(int x,int y,int l,int r)
{
    if(l==r)
    {
        t[x].s+=t[y].s;
        t[x].mx=t[x].s;
        if(t[x].mx>0)t[x].w=l;else t[x].w=0;
        return;
    }
    if(lt)
    {
        if(!ls)ls=lt;else hb(ls,lt,l,M);
    }
    if(rt)
    {
        if(!rs)rs=rt;else hb(rs,rt,M+1,r);
    }
    updata(x);
}

void dfs1(int x)
{
    for(int i=b[x];i;i=nxt[i])
        if(f[0][x]!=to[i])
        {
            dfs1(to[i]);
            hb(x+1,to[i]+1,1,w[0]);
        }
    ans[x]=t[x+1].w;
}

int main()
{
    freopen("certif.in","r",stdin);
    freopen("certif.out","w",stdout);
    read(n);read(m);
    for(int i=1;i<n;i++)
        read(x),read(y),ins(x,y),ins(y,x);
    for(int i=1;i<=m;i++)
        read(a[i].x),read(a[i].y),read(a[i].z);
    sort(a+1,a+1+m,cmp);
    w[1]=a[1].z;deep[1]=w[0]=1;
    for(int i=1;i<=m;i++)
    {
        if(a[i].z!=w[w[0]])w[++w[0]]=a[i].z;
        a[i].z=w[0];
    }
    dfs(1);
    for(int j=1;j<=16;j++)
        for(int i=1;i<=n;i++)
            f[j][i]=f[j-1][f[j-1][i]];
    tot=n+1;    
    for(int i=1;i<=m;i++)
    {
        ops=a[i].z;opx=1;
        x=a[i].x;y=a[i].y;
        L=lca(x,y);
        add(x+1,1,w[0]);
        add(y+1,1,w[0]);
        opx=-1;
        add(f[0][L]+1,1,w[0]);
        add(L+1,1,w[0]);
    }

    dfs1(1);

    for(int i=1;i<=n;i++)
        write(ans[i]?w[ans[i]]:0),putchar('\n');
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值