【GDOI2017模拟二试4.12】树上路径(点剖)

来源:互联网 发布:联通有什么软件 编辑:程序博客网 时间:2024/06/06 06:59

Description

给定一颗n个结点的无根树,树上的每个点有一个非负整数点权,定义一条路径的价值为路径上的点权和-路径的点权最大值。
给定参数p,我们想知道,有多少不同的树上简单路径,满足它的价值恰好是p的倍数。
注意:单点算作一个路径;u ≠ v时,(u,v)和(v,u)只算一次。

Solution

本来是一道很裸的点剖题,但是很久没有打点剖了,并没有去打。
点剖的时候,每个点维护两个值,一个是最大值,还有一个是和。
然后以最大值为第一关键字排序(因为要使得这个最大值一定最大才好统计方案),然后用一个桶记录一下就好了。

Code

#include<iostream>#include<stdio.h>#include<string.h>#include<algorithm>#include<math.h>#define fo(i,a,b) for(i=a;i<=b;i++)#define fod(i,a,b) for(i=a;i>=b;i--)#define rep(i,a) for(i=first[a];i;i=next[i])using namespace std;const int maxn=1e5+7,mxx=1e7+7;int i,j,k,l,t,n,m,ans,p;int first[maxn*2],last[maxn*2],next[maxn*2],num;int a[maxn],z,dian,tot,c[mxx][2],tot1,size[maxn],g[maxn],tim;bool bz[maxn];struct node{    int da,sum;}b[maxn],d[maxn];bool cmp(node x,node y){return x.da<y.da;}void add(int x,int y){last[++num]=y,next[num]=first[x],first[x]=num;}void dfs1(int x,int y){    int i;    size[x]=1;    rep(i,x)if(!bz[last[i]]&&last[i]!=y)dfs1(last[i],x),size[x]+=size[last[i]];}void dfs2(int x,int y){    int i;bool az=1;    rep(i,x)if(!bz[last[i]]&&last[i]!=y){        if(size[last[i]]>dian/2)az=0;        dfs2(last[i],x);    }    if(az&&dian-size[x]<=dian/2)z=x;}void dfs3(int x,int y,int da,int sum){    int i;    b[++tot]=(node){da,sum};    rep(i,x)if(last[i]!=y&&!bz[last[i]])dfs3(last[i],x,max(da,g[last[i]]),(sum+a[last[i]])%p);}void fen(int x){    int i;    dfs1(x,0);dian=size[x];dfs2(x,0);x=z;    tot1=0;    bz[x]=1;    d[tot=1]=(node){g[x],a[x]};    rep(i,x){        if(bz[last[i]])continue;        tot=0;        dfs3(last[i],x,max(g[last[i]],g[x]),(a[x]+a[last[i]])%p);        fo(j,1,tot)d[++tot1]=b[j];        sort(b+1,b+1+tot,cmp);        ++tim;        fo(j,1,tot){            k=((b[j].sum-b[j].da)%p+p)%p;            if(k)k=p-k;            if(c[k][0]!=tim)c[k][0]=tim,c[k][1]=0;            ans-=c[k][1];            k=((b[j].sum-a[x])%p+p)%p;            if(c[k][0]!=tim)c[k][0]=tim,c[k][1]=0;            c[k][1]++;        }    }    sort(d+1,d+1+tot1,cmp);    ++tim;    fo(i,1,tot1){        k=((d[i].sum-d[i].da)%p+p)%p;        if(!k)ans++;else k=p-k;        if(c[k][0]!=tim)c[k][0]=tim,c[k][1]=0;        ans+=c[k][1];        k=((d[i].sum-a[x])%p+p)%p;        if(c[k][0]!=tim)c[k][0]=tim,c[k][1]=0;        c[k][1]++;    }    rep(i,x){        if(!bz[last[i]])fen(last[i]);    }}int main(){    freopen("path.in","r",stdin);    freopen("path.out","w",stdout);    scanf("%d%d",&n,&p);    fo(i,1,n-1){        scanf("%d%d",&k,&l);        add(k,l),add(l,k);    }    fo(i,1,n)scanf("%d",&a[i]),g[i]=a[i],a[i]%=p;    fen(1);    printf("%d\n",ans+n);}
1 0
原创粉丝点击