CodeChef MONOPLOY Gangsters of Treeland

题目链接

题目大意

给定一棵树( 1 1 号节点是根),一开始所有节点的颜色都不同,给出q个操作,有如下两种

  • O x O   x :将根节点到 x x 节点的路径上的所有点赋成一个全新的颜色。
  • q x:查询以 x x 为根的子树的所有点到根的所有路径中每一条经过的不同的颜色的个数的平均数。

思路

正解是LCT,但是我并不会LCT

考虑每一次的修改操作会是什么样子:根到这个点的路径一开始是一些杂色,我们把它赋成了一样的颜色。设第i次修改赋的颜色是 i i ,那我们并不关心所有最大颜色小于i的“ i i 的父亲”,因为他们全都被重新覆盖了。
那么这就转化成了一个线段树维护DFS序,区间查询最大值的问题。
每次覆盖的时候,通过倍增暴力跳到每一个颜色链的顶端继续跳。
那么再考虑第二种操作。
可以发现x的所有子树都包含 x x → 根这一路径上的答案,那么我们还是只需要统计子树内部即可,用一个区间加减区间求和的线段树即可。

复杂度大概是 O(nlog2n) O ( n l o g 2 n )

坑点
  • 很考验码力啊。。。写了5K
  • 线段树错过
  • 倍增如果不在DFS里预处理是会T的。。。
  • 对于memset的使用一定要小心小心再小心。。因为是个暴力写法一旦写丑了就T了。。。
代码
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<map>
#include<queue>
#include<vector>
#include<stack>
#include<set>
#include<cctype>
#define INF 0x3f3f3f3f
#define inf 0x3f
#define fi first
#define se second
#define mp make_pair
#define ll long long
#define ull unsigned long long
#define pb push_back

using namespace std;

inline int read()
{
    int f=1,sum=0;
    char c=getchar();
    while (!isdigit(c)) {if (c=='-') f=-1;c=getchar();}
    while (isdigit(c)) {sum=sum*10+c-'0';c=getchar();}
    return sum*f;
}
#define MAXN 100010
struct edge{
    int next,to;
}e[MAXN*2];
int head[MAXN],cnt;
void addedge(int u,int v)
{
    e[++cnt].next=head[u];
    e[cnt].to=v;
    head[u]=cnt;
}
struct SegmenttreeMax{
    int f[4*MAXN];
    void build(int root,int left,int right)
    {
        f[root]=0;
        if (left==right)
            return ;
        int mid=(left+right)>>1;
        build(2*root,left,mid);
        build(2*root+1,mid+1,right);
    }
    void update(int root,int left,int right,int x,int y)
    {
        if (left==right)
        {
            f[root]=y;
            return ;
        }
        int mid=(left+right)>>1;
        if (x<=mid)
            update(2*root,left,mid,x,y);
        else
            update(2*root+1,mid+1,right,x,y);
        f[root]=max(f[2*root],f[2*root+1]);
    }
    int query(int root,int left,int right,int qleft,int qright)
    {
        if (qleft<=left && right<=qright)
            return f[root];
        int mid=(left+right)>>1;
        if (qright<=mid)
            return query(2*root,left,mid,qleft,qright);
        else if (qleft>mid)
            return query(2*root+1,mid+1,right,qleft,qright);
        else
        {
            int ans1,ans2;
            ans1=query(2*root,left,mid,qleft,mid);
            ans2=query(2*root+1,mid+1,right,mid+1,qright);
            return max(ans1,ans2);
        }
    }
}Max;
int init[MAXN];
struct SegmenttreeSum{
    ll f[4*MAXN],g[4*MAXN];
    void build(int root,int left,int right)
    {
        g[root]=0;
        if (left==right)
        {
            f[root]=init[left];
            return ;
        }
        int mid=(left+right)>>1;
        build(2*root,left,mid);
        build(2*root+1,mid+1,right);
        f[root]=f[2*root]+f[2*root+1]; 
    }
    void pushdown(int root,int x)
    {
        g[2*root]+=g[root],g[2*root+1]+=g[root];
        f[2*root]+=(ll)g[root]*(x-x/2),f[2*root+1]+=(ll)g[root]*(x/2);
        g[root]=0;
    }
    void update(int root,int left,int right,int qleft,int qright,ll x)
    {
        if (qleft<=left && right<=qright)
        {
            f[root]+=(ll)(right-left+1)*x;
            g[root]+=x;
            return ;
        }
        if (g[root])
            pushdown(root,right-left+1);
        int mid=(left+right)>>1;
        if (qright<=mid)
            update(2*root,left,mid,qleft,qright,x);
        else if (qleft>mid)
            update(2*root+1,mid+1,right,qleft,qright,x);
        else
        {
            update(2*root,left,mid,qleft,mid,x);
            update(2*root+1,mid+1,right,mid+1,qright,x);
        }
        f[root]=f[2*root]+f[2*root+1];
    }
    ll query(int root,int left,int right,int qleft,int qright)
    {
        if (qleft<=left && right<=qright)
            return f[root];
        if (g[root])
            pushdown(root,right-left+1);
        int mid=(left+right)>>1;
        if (qright<=mid)
            return query(2*root,left,mid,qleft,qright);
        else if (qleft>mid)
            return query(2*root+1,mid+1,right,qleft,qright);
        else
        {
            ll ans1,ans2;
            ans1=query(2*root,left,mid,qleft,mid);
            ans2=query(2*root+1,mid+1,right,mid+1,qright);
            return ans1+ans2;
        }
    }
}Sum;
#define LOG 20
int fa[MAXN][LOG],sz[MAXN],st[MAXN],ed[MAXN],ti;
set<pair<pair<int,int>,int> > s[MAXN];
void dfs(int x,int father,int deep)
{
    for (int j=1;j<LOG;j++)
        fa[x][j]=fa[fa[x][j-1]][j-1];   
    ti++;
    st[x]=ti;
    init[ti]=deep;
    sz[x]=1;
    for (int i=head[x];i;i=e[i].next)
    {
        int v=e[i].to;
        if (v==father) continue;
        fa[v][0]=x;
        dfs(v,x,deep+1);
        sz[x]+=sz[v];
        s[x].insert(mp(mp(st[v],ed[v]),v));
    }
    ed[x]=ti;
}
int q[MAXN],Cnt;
void pre(int n)
{
    memset(cle,0,sizeof(cle));
    ti=0;
    Cnt=0;
    for (int i=1;i<=n;++i)
        s[i].clear();
    memset(head,0,sizeof(head));
    cnt=0;
    for (int i=1;i<n;i++)
    {
        int u=read(),v=read();
        u++,v++;
        addedge(u,v),addedge(v,u);
    }
}
char opt[5];
int main()
{
    int T;
    scanf("%d",&T);
    while (T--)
    {
        int n,m,x;
        scanf("%d",&n);
        pre(n); 
        fa[1][0]=1;
        dfs(1,0,0);
        Max.build(1,1,n);
        Sum.build(1,1,n);
        scanf("%d",&m);
        while (m--)
        {
            scanf("%s%d",opt,&x);
            x++; 
            if (opt[0]=='q')
            {
                ll num=Sum.query(1,1,n,st[x],ed[x]);
                double ans=1.0*num/sz[x];
                printf("%.8lf\n",ans); 
            }
            else
            {
                int t=x;
                while (1)
                {
                    int maxcol=Max.query(1,1,n,st[t],ed[t]);
                    if (!maxcol)
                    {
                        if (t!=1)
                            Sum.update(1,1,n,st[t],ed[t],-1);
                        else
                            break;
                        t=fa[t][0];
                    }
                    else
                    {
                        int nowcol=q[maxcol];
                        if (nowcol!=t)
                        {
                            set<pair<pair<int,int>,int> >::iterator it=s[t].lower_bound(mp(mp(st[nowcol],1000000000),0));
                            it--;
                            int y=it->second;
                            Sum.update(1,1,n,st[y],ed[y],1);
                        }
                        int tt=t;
                        for (int i=LOG-1;~i;--i)
                            if (Max.query(1,1,n,st[fa[tt][i]],ed[fa[tt][i]])==maxcol)
                                tt=fa[tt][i];
                        if (tt==1) break;
                        Sum.update(1,1,n,st[tt],ed[tt],-1);
                        t=fa[tt][0];
                    } 
                }
                q[++Cnt]=x;
                Max.update(1,1,n,st[x],Cnt);
            }
        }
    }
    return 0;
}

**抽空学学LCT**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值