JZOJ5365. 【GDOI2018模拟9.14】通信 线段树+重链剖分

33 篇文章 0 订阅
11 篇文章 0 订阅

这里写图片描述

我们考虑每条边的贡献。
把已经走过的点染色为1,未走过的为0,那么我们对于序号序列维护一颗线段树,x-v这条边的贡献就是序列中不连续01序列包含x的个数,那么我们反过来,答案=总序列-经过x的极大连续序列,这个东西明显可以用线段树维护,维护一个从左右开始的最长01长度和最长连续就好了。
问题是直接遍历操作明显会T,我们重链剖分一下,对于一个点,先把其他的轻儿子暴力扫掉,然后再递归处理重儿子。
时间复杂度是O(nlog^2),线段树一个log+重链剖分一个log。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e5+5;
const int mo=1e9+7;
int n,m,head[N],next[N],go[N],size[N];
int son[N],cnt,dep[N];
typedef long long ll;
struct node
{
    int l,r;
    ll s;
    int l0,l1,r0,r1,mx;
}t[N*5];

ll ans,ny;
inline ll pow(ll a,int b)
{
    ll ret=1;
    while (b)
    {
        if (b&1)ret=ret*a%mo;
        a=a*a%mo;
        b>>=1;
    }
    return ret;
}
inline ll get(int x)
{
    return 1ll*x*(x+1)/2;
}
inline void add(int x,int y)
{
    go[++cnt]=y;
    next[cnt]=head[x];
    head[x]=cnt;
}
inline void dfs(int x,int fa)
{
    dep[x]=dep[fa]+1;
    size[x]=1;
    int mx=0;
    for(int i=head[x];i;i=next[i])
    {
        int v=go[i];
        if (v!=fa)
        {
            dfs(v,x);
            size[x]+=size[v];
        }
    }
}
inline void dfs1(int x,int fa)
{
    int k=0;
    for(int i=head[x];i;i=next[i])
    {
        int v=go[i];
        if (v!=fa)
        {
            if (size[v]>size[k])k=v;
        }
    }
    if (!k)return;
    son[x]=k;
    for(int i=head[x];i;i=next[i])
    {
        int v=go[i];
        if (v!=fa)dfs1(v,x);
    }
}
inline void pushup(int x,int l,int r)
{
    int mid=(l+r)>>1;
    t[x].s=t[x<<1].s+t[x<<1|1].s-get(t[x<<1].r0)-get(t[x<<1].r1)-get(t[x<<1|1].l0)-get(t[x<<1|1].l1);
    t[x].s+=get(t[x<<1].r0+t[x<<1|1].l0)+get(t[x<<1].r1+t[x<<1|1].l1);
    t[x].l0=t[x<<1].l0;
    t[x].l1=t[x<<1].l1;
    t[x].r0=t[x<<1|1].r0;
    t[x].r1=t[x<<1|1].r1;
    if (t[x].l0==mid-l+1)t[x].l0+=t[x<<1|1].l0;
    if (t[x].l1==mid-l+1)t[x].l1+=t[x<<1|1].l1;
    if (t[x].r0==r-mid)t[x].r0+=t[x<<1].r0;
    if (t[x].r1==r-mid)t[x].r1+=t[x<<1].r1;
}
inline void build(int x,int l,int r)
{
    t[x].l=l,t[x].r=r;
    t[x].l0=t[x].r0=r-l+1;
    t[x].s=get(r-l+1);
    if (l==r)return;
    int mid=(l+r)>>1;
    build(x<<1,l,mid);
    build(x<<1|1,mid+1,r);
}
inline void change(int x,int pos,int v)
{
    if (t[x].l==t[x].r)
    {
        if (!v)t[x].l0=t[x].r0=t[x].s=1,t[x].l1=t[x].r1=0;
        else t[x].l1=t[x].r1=t[x].s=1,t[x].l0=t[x].r0=0;
        return;
    }
    int mid=(t[x].l+t[x].r)>>1;
    if (pos<=mid)change(x<<1,pos,v);
    else change(x<<1|1,pos,v);
    pushup(x,t[x].l,t[x].r);
}
inline void fill(int x,int fa,int y)
{
    change(1,x,y);
    for(int i=head[x];i;i=next[i])
    {
        int v=go[i];
        if (v!=fa)fill(v,x,y);
    }
}
inline void solve(int x,int fa)
{
    for(int i=head[x];i;i=next[i])
    {
        int v=go[i];
        if (v!=fa&&v!=son[x])
        {
            solve(v,x);
            fill(v,x,0);
        }
    }
    if (son[x])solve(son[x],x);
    change(1,x,1);
    for(int i=head[x];i;i=next[i])
    {
        int v=go[i];
        if (v!=son[x]&&v!=fa)fill(v,x,1);
    }
    ll tot=1ll*(n+1)*n/2-t[1].s;
    if (x!=1)ans=(ans+1ll*2*tot*ny)%mo;
}
int main()
{
     freopen("communicate.in","r",stdin);
     freopen("communicate.out","w",stdout);
    scanf("%d",&n);
    fo(i,1,n-1)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y),add(y,x);
    }
    dfs(1,0);
    dfs1(1,0);
    ny=n*(n+1)/2;
    ny=pow(ny,mo-2);
    build(1,1,n);
    solve(1,0);
    printf("%lld\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值