codeforces 502 g The Tree

题解:

一道优秀的题目

有几种做法:

1.维护后缀和

刚开始我想的是维护前缀和

然后用$sum[x]-sum[y]>=dep[x]-dep[y]$来做

但是这样子树赋值为0这个操作就很难进行了

因为你查找的是链上最小值,所以不改子树上面的节点是做不了的

那我们换一种方式,单点改,查询区间最大后缀和

这样子树染白的时候我们就可以在x处减去一个上面的最大后缀和,那么就对子树没有影响了

然后再把子树清空一下就可以

其实这个东西就是动态dp。。。

$$f(v)=max(f(x)-1,0)+v[v]$$

这个只用查询链

所以我们只需要维护$f[v]=MAX(f[top]必选时的最大值k1,任意情况最大值k2)$就可以了(v都是必选点)

 

#include <bits/stdc++.h>
using namespace std;
#define rint register int
#define IL inline
#define rep(i,h,t) for(int i=h;i<=t;i++)
#define dep(i,t,h) for(int i=t;i>=h;i--)
#define ll long long
#define me(x) memset(x,0,sizeof(x))
#define mep(x,y) memcpy(x,y,sizeof(y))
#define mid ((h+t)>>1)
namespace IO{
    char ss[1<<24],*A=ss,*B=ss;
    IL char gc()
    {
        return A==B&&(B=(A=ss)+fread(ss,1,1<<24,stdin),A==B)?EOF:*A++;
    }
    template<class T> void read(T &x)
    {
        rint f=1,c; while (c=gc(),c<48||c>57) if (c=='-') f=-1; x=(c^48);
        while (c=gc(),c>47&&c<58) x=(x<<3)+(x<<1)+(c^48); x*=f; 
    }
    char sr[1<<24],z[20]; ll Z,C1=-1;
    template<class T>void wer(T x)
    {
        if (x<0) sr[++C1]='-',x=-x;
        while (z[++Z]=x%10+48,x/=10);
        while (sr[++C1]=z[Z],--Z);
    }
    IL void wer1()
    {
        sr[++C1]=' ';
    }
    IL void wer2()
    {
        sr[++C1]='\n';
    }
    template<class T>IL void maxa(T &x,T y) {if (x<y) x=y;}
    template<class T>IL void mina(T &x,T y) {if (x>y) x=y;} 
    template<class T>IL T MAX(T x,T y){return x>y?x:y;}
    template<class T>IL T MIN(T x,T y){return x<y?x:y;}
};
using namespace IO;
const int N=2.1e5;
struct re{
    int a,b;
}e[N*2];
int head[N],l,n,m;
IL void arr(int x,int y)
{
    e[++l].a=head[x];
    e[l].b=y;
    head[x]=l;
}
struct re2{
    int a[2];
    re2() { a[0]=a[1]=0; }
    re2(int x,int y) {a[0]=x; a[1]=y;};
    re2 operator *(const re2 &o) const
    {
        re2 c;
        c.a[0]=a[0]+o.a[0]; c.a[1]=MAX(a[1]+o.a[0],o.a[1]);
        return c;
    }
};
int fa[N],num[N],son[N],dfn[N],top[N],cnt;
void dfs(int x,int y)
{
    num[x]=1; fa[x]=y;
    for (rint u=head[x];u;u=e[u].a)
    {
        int v=e[u].b;
        if (v!=y)
        { 
          dfs(v,x);
          num[x]+=num[v];
          if (num[son[x]]<num[v]) son[x]=v;
        }
    }
}
void dfs1(int x,int y,int z)
{
    top[x]=y; dfn[x]=++cnt;
    if (son[x]) dfs1(son[x],y,x);
    for (rint u=head[x];u;u=e[u].a)
    {
        int v=e[u].b;
        if (v!=z&&v!=son[x])
        {
            dfs1(v,v,x);
        }
    }
}
struct sgt{
    re2 sum[N*4];
    bool lazy[N*4];
    #define updata(x) sum[x]=sum[x*2]*sum[x*2+1]
    IL void down(int x,int h,int t)
    {
        if (lazy[x])
        {
            lazy[x*2]=lazy[x*2+1]=1; lazy[x]=0;
            sum[x*2]=re2(-(mid-h+1),-1);
            sum[x*2+1]=re2(-(t-mid),-1);
        }
    }
    void build(int x,int h,int t)
    {
        if (h==t) { sum[x]=re2(-1,-1); return;}
        build(x*2,h,mid); build(x*2+1,mid+1,t);
        updata(x);
    }
    void change(int x,int h,int t,int pos,int k)
    {
        if (h==t) { sum[x]=re2(k,k);return; }
        down(x,h,t);
        if (pos<=mid) change(x*2,h,mid,pos,k);
        else change(x*2+1,mid+1,t,pos,k);
        updata(x);
    }
    re2 query(int x,int h,int t,int h1,int t1)
    {
        if (h1<=h&&t<=t1) return sum[x];
        down(x,h,t);
        if (h1<=mid&&mid<t1) return query(x*2,h,mid,h1,t1)*query(x*2+1,mid+1,t,h1,t1);
        else if (h1<=mid) return query(x*2,h,mid,h1,t1);
        else return query(x*2+1,mid+1,t,h1,t1);
    }
    void push(int x,int h,int t,int h1,int t1)
    {
        if (h1<=h&&t<=t1) { lazy[x]=1; sum[x]=re2(-(t-h+1),-1); return;}
        down(x,h,t);
        if (h1<=mid) push(x*2,h,mid,h1,t1); 
        if (mid<t1) push(x*2+1,mid+1,t,h1,t1);
        updata(x);
    }
}S;
IL int query(int x)
{
    int ans=-1;
    re2 p1=re2(0,-1);
    while (x)
    {
        re2 p=S.query(1,1,n,dfn[top[x]],dfn[x]);
        p1=p*p1;
        ans=max(ans,p1.a[1]);
        x=fa[top[x]];
    }
    return ans;
}
int main()
{
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
    read(n); read(m);
    rep(i,2,n)
    {
        int x;
        read(x);
        arr(i,x); arr(x,i);
    }
    dfs(1,0); dfs1(1,1,0);
    S.build(1,1,n);
    rep(i,1,m)
    {
        int kk,x;
        read(kk); read(x);
        if (kk==1)
        {
            S.change(1,1,n,dfn[x],S.query(1,1,n,dfn[x],dfn[x]).a[0]+1);
        }
        if (kk==2)
        {
            S.push(1,1,n,dfn[x],dfn[x]+num[x]-1);
            int ans=query(x);
            S.change(1,1,n,dfn[x],-ans-2);
        }
        if (kk==3)
        {
            if (query(x)>=0) cout<<"black"<<endl;
            else cout<<"white"<<endl;
        }
    }
    return 0;
}

 

 

 

2.操作分块

分块这个东西有的时候的确简单巧妙。。

但一般我也不会去想分块。。

这个是看了别人题解的。。

我们以每$\sqrt{n}$个元素分一组

对块内的操作,我们等待$\sqrt{n}$都做完了再把这$\sqrt{n}$个操作加入树中

对于当前的询问,我们只需要树内的$\sqrt{n}$个节点的信息就可以了

我们可以建立虚树维护

本来打算学树上分块的。。。但发现这东西并没有啥用

会线段树合并/dsu on tree/树上莫队 应该不会也没啥关系

像这种利用虚树的题目还是比较多的

posted @ 2018-12-15 18:47  尹吴潇  阅读(188)  评论(0编辑  收藏  举报