JZOJ 5463. 【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

Solution

  • 看到这题,区间操作操作又在树上,那么就想到树链剖分+线段树。

  • 在每条链链头打一个加标记,在链尾的后一位打一个减标记。

  • 这样得到的DFS序上就有了很多标记,我们直接在序列上查找、修改即可。

  • 每到一个点,就进行在这个点上的操作,之后查询最大值的位置。

  • 注意 zi 的值很大,需要进行离散化。

Code

#include
#include
#include
using namespace std;
const int N=1e5+1;
struct data
{
    int x,y;
}f[N<<2];
int tot,num;
int first[N],next[N<<1],en[N<<1];
int fa[N],size[N],dep[N];
int son[N],tree[N],pre[N],top[N];
int h[N],id[N],p[N],ans[N],a[N],b[N],c[N];
vector<int>g[N];
inline int read()
{
    int X=0,w=1; char ch=0;
    while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
    return X*w;
}
inline void write(int x)
{
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
inline void insert(int x,int y)
{
    next[++tot]=first[x];
    first[x]=tot;
    en[tot]=y;
}
inline void dfs1(int x)
{
    dep[x]=dep[fa[x]]+1,size[x]=1;
    for(int i=first[x];i;i=next[i])
        if(en[i]^fa[x])
        {
            fa[en[i]]=x;
            dfs1(en[i]);
            size[x]+=size[en[i]];
            if(!son[x] || size[en[i]]>size[son[x]]) son[x]=en[i];
        }
}
inline void dfs2(int x,int y)
{
    top[pre[tree[x]=++num]=x]=y;
    if(!son[x]) return;
    dfs2(son[x],y);
    for(int i=first[x];i;i=next[i])
        if(en[i]^fa[x] && en[i]^son[x]) dfs2(en[i],en[i]);
}
inline data merge(data x,data y)
{
    return x.x>=y.x?x:y;
}
inline void change(int v,int l,int r,int x,int y)
{
    if(l==r)
    {
        if(f[v].x+=y) f[v].y=x; else f[v].y=0;
        return;
    }
    int mid=(l+r)>>1;
    if(x<=mid) change(v<<1,l,mid,x,y); else change(v<<1|1,mid+1,r,x,y);
    f[v]=merge(f[v<<1],f[v<<1|1]);
}
inline data find(int v,int l,int r,int x,int y)
{
    if(l>=x && r<=y) return f[v];
    int mid=(l+r)>>1;
    if(y<=mid) return find(v<<1,l,mid,x,y);
    if(x>mid) return find(v<<1|1,mid+1,r,x,y);
    return merge(find(v<<1,l,mid,x,mid),find(v<<1,mid+1,r,mid+1,y));
}
int main()
{
    int n=read(),m=read();
    for(int i=1;iint x=read(),y=read();
        insert(x,y);
        insert(y,x);
    }
    dfs1(1),dfs2(1,1);
    for(int i=1;i<=m;i++)
    {
        a[i]=read(),b[i]=read(),c[i]=read();
        h[++h[0]]=c[i];
    }
    sort(h+1,h+1+h[0]);
    id[1]=tot=1,p[1]=h[1];
    for(int i=2;i<=h[0];i++)
        if(h[i]^h[i-1]) p[id[i]=++tot]=h[i]; else id[i]=id[i-1];
    for(int i=1;i<=m;i++)
    {
        int x=a[i],y=b[i],z=c[i];
        int k=lower_bound(h+1,h+1+h[0],z)-h;
        z=id[k];
        int f1=top[x],f2=top[y];
        while(f1^f2)
        {
            if(dep[f1]1].push_back(-z);
            x=fa[f1],f1=top[x];
        }
        if(dep[x]>dep[y]) swap(x,y);
        g[tree[x]].push_back(z);
        g[tree[y]+1].push_back(-z);
    }
    for(int i=1;i<=num;i++)
    {
        for(int j=0;jint x=abs(g[i][j]),y=g[i][j]>0?1:-1;
            change(1,1,tot,x,y);
        }
        data t=find(1,1,tot,1,tot);
        ans[pre[i]]=p[t.y];
    }
    for(int i=1;i<=n;i++) write(ans[i]),putchar('\n');
    return 0;
}

你可能感兴趣的:(树链剖分,线段树,图论,离散化,树链剖分,线段树,离散化)