【XSY2665】没有上司的舞会 LCT DP

题目大意

  有一棵树,最开始只有一个点。每次会往这棵树中加一个点,总共 n 次。输出每次加点后树的最大独立集大小。

  强制在线。

  n300000

题解

  显然是LCT。

  那么要维护什么呢?

  先看看DP方程:设 fi,0 为以 i 为根的子树中i这个点不选的答案, fi,1 i 这个点选的答案。显然

fi,0fi,1=vmax(fv,0,fv,1)=1+vfv,0

  先看看一条链要怎么做。设 si,j 为某一段中第一个点的状态为 i ,在后面补一个状态为j的点时这一段的贡献。这个东西很容易合并。

  只有一个点时

s0,0s0,1s1,0s1,1=0=0=1=

  那树上要怎么做?

  容易观察到 i 的各个儿子之间是互不影响的。可以像这道题一样,把整棵树剖成轻重链,每个点的贡献要加上这个点的轻儿子的贡献。

  access和link时处理一下即可。

  时间复杂度:O(nlogn)

题解

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<cmath>
#include<functional>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
void sort(int &a,int &b)
{
    if(a>b)
        swap(a,b);
}
void open(const char *s)
{
#ifndef ONLINE_JUDGE
    char str[100];
    sprintf(str,"%s.in",s);
    freopen(str,"r",stdin);
    sprintf(str,"%s.out",s);
    freopen(str,"w",stdout);
#endif
}
int rd()
{
    int s=0,c;
    while((c=getchar())<'0'||c>'9');
    do
    {
        s=s*10+c-'0';
    }
    while((c=getchar())>='0'&&c<='9');
    return s;
}
void put(int x)
{
    if(!x)
    {
        putchar('0');
        return;
    }
    static int c[20];
    int t=0;
    while(x)
    {
        c[++t]=x%10;
        x/=10;
    }
    while(t)
        putchar(c[t--]+'0');
}
int upmin(int &a,int b)
{
    if(b<a)
    {
        a=b;
        return 1;
    }
    return 0;
}
int upmax(int &a,int b)
{
    if(b>a)
    {
        a=b;
        return 1;
    }
    return 0;
}
struct p
{
    ll s11,s12,s21,s22;
    p()
    {
        s11=s12=s21=s22=0;
    }
};
p merge(p a,p b)
{
    p c;
    c.s11=max(a.s11+b.s11,a.s12+b.s21);
    c.s12=max(a.s11+b.s12,a.s12+b.s22);
    c.s21=max(a.s21+b.s11,a.s22+b.s21);
    c.s22=max(a.s21+b.s12,a.s22+b.s22);
    return c;
}
void add(p &a,ll s11,ll s12,ll s21,ll s22)
{
    a.s11+=s11;
    a.s12+=s12;
    a.s21+=s21;
    a.s22+=s22;
}
namespace lct
{
    int f[300010];
    int a[300010][2];
    p v[300010];
    p s[300010];
    int root(int x)
    {
        return !f[x]||(a[f[x]][0]!=x&&a[f[x]][1]!=x);
    }
    void mt(int x)
    {
        s[x]=v[x];
        if(a[x][0])
            s[x]=merge(s[a[x][0]],s[x]);
        if(a[x][1])
            s[x]=merge(s[x],s[a[x][1]]);
    }
    void rotate(int x)
    {
        int p=f[x];
        int q=f[p];
        int ps=(x==a[p][1]);
        int qs=(p==a[q][1]);
        int ch=a[x][ps^1];
        if(!root(p))
            a[q][qs]=x;
        a[x][ps^1]=p;
        a[p][ps]=ch;
        if(ch)
            f[ch]=p;
        f[p]=x;
        f[x]=q;
        mt(p);
    }
    void splay(int x)
    {
        while(!root(x))
        {
            int p=f[x];
            if(!root(p))
            {
                int q=f[p];
                if((p==a[q][1])==(x==a[p][1]))
                    rotate(p);
                else
                    rotate(x);
            }
            rotate(x);
        }
        mt(x);
    }
    void access(int x)
    {
        int y=x;
        int t=0;
        while(x)
        {
            splay(x);
            add(v[x],max(s[a[x][1]].s21,s[a[x][1]].s22),max(s[a[x][1]].s21,s[a[x][1]].s22),max(max(s[a[x][1]].s11,s[a[x][1]].s12),max(s[a[x][1]].s21,s[a[x][1]].s22)),max(max(s[a[x][1]].s11,s[a[x][1]].s12),max(s[a[x][1]].s21,s[a[x][1]].s22)));
            add(v[x],-max(s[t].s21,s[t].s22),-max(s[t].s21,s[t].s22),-max(max(s[t].s11,s[t].s12),max(s[t].s21,s[t].s22)),-max(max(s[t].s11,s[t].s12),max(s[t].s21,s[t].s22)));
            a[x][1]=t;
            mt(x);
            t=x;
            x=f[x];
        }
        splay(y);
    }
    void link(int x,int y)
    {
        v[x].s11=-0x7fffffff;
        v[x].s12=1;
        v[x].s21=0;
        v[x].s22=0;
        mt(x);
        access(y);
        f[x]=y;
        a[y][1]=x;
//      add(v[y],1,0);
        mt(y);
    }
};
int main()
{
    open("b");
    int n,type;
    scanf("%d%d",&n,&type);
    int i,x;
    int ans=0;
    lct::v[1].s11=-0x7fffffff;
    lct::v[1].s12=1;
    lct::v[1].s21=0;
    lct::v[1].s22=0;
    lct::mt(1);
    for(i=2;i<=n+1;i++)
    {
        scanf("%d",&x);
        if(type)
            x^=ans;
        x++;
        lct::link(i,x);
        lct::access(1);
        ans=max(max(lct::s[1].s11,lct::s[1].s21),max(lct::s[1].s12,lct::s[1].s22));
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值