【JZOJ 4388】 染色

Description

这里写图片描述
N105

Analysis

暴力竟然跑得极快极快,理论 O(n2) 的比 O(nlog2n) 的快得多得多。。。
其实因为这里的 N 只有105,而我的暴力又是找过重心以后的,所以实际情况比 O(n2) 要小得多。
GDOI2015有一道题,也是 N105 ,可是标程是 O(n2) 的,而且是后缀数组,常数较大,竟然可以过。。。
有一题, N500 ,要跑floyd,但是 O(5003) 比较玄学,依然能过。
回到正题,此题可以用动态点分治做,当然,也可以用一种神奇的算法链剖维护。
我打了后者。
首先 Ans=numdis[v]+sum2ublackdis[lca(u,v)]
前面不带 的部分直接乱搞,后面的具体是每次染黑一个点就把该点到根的所有点的标记全部+1,查询就是该点到根的所有点到起父亲的边的边权乘标记个数。
其实是一种线段覆盖的思想。

Code

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define efo(i,v) for(int i=last[v];i;i=next[i])
using namespace std;
typedef long long ll;
const int N=100010,M=N*2;
int n,m,tot,to[M],wei[M],next[M],last[N];
int fa[N],w[N],re[N],son[N],size[N],top[N];
ll c[N],dis[N];
struct segment
{
    ll x,y,lz;
}a[N*4];
bool black[N];
void link(int u,int v,int w)
{
    to[++tot]=v,wei[tot]=w,next[tot]=last[u],last[u]=tot;
}
void dfs1(int v,int from,int x)
{
    int k=0;
    size[v]=1,dis[v]=x;
    efo(i,v)
    {
        int u=to[i];
        if(u==from) continue;
        dfs1(u,v,x+wei[i]);
        size[v]+=size[u];
        if(size[u]>size[k]) k=u;
    }
    son[v]=k;
}
void dfs2(int v,int from,int p)
{
    top[v]=p,w[v]=++m,re[m]=v;
    if(!son[v]) return;
    dfs2(son[v],v,p);
    efo(i,v)
    {
        int u=to[i];
        if(u==from || u==son[v]) continue;
        dfs2(u,v,u);
    }
}
void build(int v,int l,int r)
{
    if(l==r)
    {
        a[v].y=c[re[l]];
        return;
    }
    int mid=(l+r)>>1;
    build(v+v,l,mid),build(v+v+1,mid+1,r);
    a[v].y=a[v+v].y+a[v+v+1].y;
}
void down(int v,int l,int r)
{
    if(a[v].lz==0) return;
    int mid=(l+r)>>1;
    a[v+v].x+=a[v+v].y*a[v].lz,a[v+v].lz+=a[v].lz;
    a[v+v+1].x+=a[v+v+1].y*a[v].lz,a[v+v+1].lz+=a[v].lz;
    a[v].lz=0;
}
void modify(int v,int l,int r,int x,int y)
{
    if(l==x && r==y)
    {
        a[v].x+=a[v].y,a[v].lz++;
        return;
    }
    down(v,l,r);
    int mid=(l+r)>>1;
    if(y<=mid) modify(v+v,l,mid,x,y);
    else
    if(x>mid) modify(v+v+1,mid+1,r,x,y);
    else
    modify(v+v,l,mid,x,mid),modify(v+v+1,mid+1,r,mid+1,y);
    a[v].x=a[v+v].x+a[v+v+1].x;
}
void change(int v)
{
    for(;top[v]>=1;v=fa[top[v]])
    {
        modify(1,1,n,w[top[v]],w[v]);
        if(fa[top[v]]<1) break;
    }
}
ll find(int v,int l,int r,int x,int y)
{
    if(l==x && r==y) return a[v].x;
    down(v,l,r);
    int mid=(l+r)>>1;
    if(y<=mid) return find(v+v,l,mid,x,y);
    else
    if(x>mid) return find(v+v+1,mid+1,r,x,y);
    else
    return find(v+v,l,mid,x,mid)+find(v+v+1,mid+1,r,mid+1,y);
}
ll query(int v)
{
    ll t=0;
    for(;top[v]>=1;v=fa[top[v]])
    {
        t+=find(1,1,n,w[top[v]],w[v]);
        if(fa[top[v]]<1) break;
    }
    return t;
}
int main()
{
    int _,t,x;
    ll num=0,sum=0;
    scanf("%d %d",&n,&_);
    fo(i,2,n) scanf("%d",&fa[i]),fa[i]++;
    fo(i,2,n)
    {
        scanf("%lld",&c[i]);
        link(i,fa[i],c[i]),link(fa[i],i,c[i]);
    }
    dfs1(1,0,0);
    dfs2(1,0,1);
    build(1,1,n);
    while(_--)
    {
        scanf("%d %d",&t,&x);x++;
        if(t==1)
        {
            if(black[x]) continue;
            black[x]=1,num++,sum+=dis[x];
            change(x);
        }
        else
        printf("%lld\n",num*dis[x]+sum-2*query(x));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值