总的来说是比较失败的一次考试,两个半小时死刚T1,剩下不到四十分钟写T3,很仓促,爆了0。时间分配严重不均匀导致T3虽然想到了部分解法但是没有来得及实现和调试。

  大概考试过程就是先看了三道题,发现T3很简单,然后开始想,三分钟秒掉式子,大概7:22的时候就切掉了(事实证明它除了一个特判的确切掉了,而且才开考7分钟我还看了表)。然后回去看第一题,发现贡献分了两半,矩形内部和矩形之间的,矩形内部可以$O(1)$得到,矩形之间的可以$O(n^2)$算出来,但是不是严格$O(n^2)$,因为矩形之间的临接边很少,没有的时候就跳就可以了。结果写了二十分钟过不了大样例,当时绝望了,调试的时候发现矩形越多数值越小我就很纳闷,但是最终也没调出来(其实是矩形边界处理出了问题,出现了负数)。于是去做T2,基本也是秒出解(虽然不太完善但基本和正解差不多,除了合并方式上),然后十分钟敲了两颗树开始维护,只过了小样例,还是大的过不去,一直调到考试结束,于是T3愉快爆0。

  下面是题解,但是这个东西仿佛不是这次考试最重要的收获,我觉得应该是一些关于心态方面的启发,这些我会在最后说。

T1:

  是真的暴力分情况而且没水平我实在懒得写

  贡献分两部分,

  内部贡献=$ 2(x2-x1)(y2-y1) $

  边界贡献=$ 2(边界长度)+[一端坐标不一样]+[另一端坐标不一样] $

  暴力枚举加起来即可,到没有边界的时候$break$掉即可

 

T2:

  比较强的一道题,考场上想到了时间轴建树,想到了二分$size$查排名前$k$的数,想到了合并(假的线段树合并,应该是启发式合并)。

  想了很久一直没有结果,因为我的线段树合并是以不遍历全部节点为复杂度保障,但是合并了之后又无法断定哪些点是具有贡献的,这让我很难受,最后我取了暴力的方法,暴力查哪些节点是被重复计算的,贡献置0,但是会重复计算,比如说在某一个子树的时候某个时间的已经被计算过是无用的,那么接下来子树合并的过程中,他永远不可能继续有用,那么就可以不查了,但是我考场上没有想到这一点,也许这样可以挽救我死了的线段树合并思路。

  剩下的就是启发式合并了。合并什么呢?我们不合并线段树,合并操作,或许你觉得这很暴力,但是他有一定的复杂度保证$(O(nlog_2n))$。

  启发式合并其实很简单,就是把小的塞到大的里面,这样的话可以使得代价降到最低,复杂度怎么来的呢?假设我们每次合并的复杂度是$(O(n))$,而最劣的情况就是我每次拿一个和你一样大的塞你,这样我每次增大一倍,也就是会增加$(log_2n)$次,那么总的复杂度就是$O(nlog_2n)$

  复杂度是有保障的,那么怎么合并呢?考虑树上的“大”是什么意思。颓了一篇很不详细的题解就明白了。对,是重儿子。重儿子是你最大的子树,其他的子树在处理完成之后直接在线段树上打标记删除掉即可,而重儿子万万不能删除,因为你的操作要建立在重儿子的基础上操作,这样能够保证复杂度。

  我当时没有想到维护线段树的方式这么暴力。。。

  把自己的操作插入重儿子的$vector$(一般情况下重儿子操作比我身上的要多,毕竟我就一个节点)然后交换位置,再把其他轻儿子的操作插入我的$vector$,这样完成了多次启发式合并,复杂度保证是$O(nlog_2n)$。

  刚才突然想了一下,轻重划分不应当以字节点的大小作为划分依据,而应当用子节点$vector$大小,他快了$100ms$。

  而每次操作都对应线段树的一次修改或者删除。

  总复杂度就是$O(nlog_2^2n)$

  每次递归的进行这个过程,就可以完美解决这道题。

  说的比较潦草,不懂评论区。

 

#include<cstdio>
#include<cmath>
#include<vector>
#include<iostream>
#include<map>
using namespace std;
const int maxn=1e5+5,INF=0x3f3f3f3f;
map<int,int> mp;
vector<int> bl[maxn],dr[maxn];
int n,m,q,cnt,tot,x,y,a[maxn],first[maxn],ans[maxn],sz[maxn],son[maxn],t[maxn];
struct road{
    int u,t,nxt;
}eage[maxn<<1];
void add(int x,int y)
{
    eage[++tot].u=x;
    eage[tot].t=y;
    eage[tot].nxt=first[x];
    first[x]=tot;
}
struct SegmentTree{
    int tot,data[maxn<<2],sz[maxn<<2],f[maxn<<2];
    void FoundData(int x)
    {
        data[x]=data[x<<1]+data[x<<1|1];
        sz[x]=sz[x<<1]+sz[x<<1|1];
    }
    void LazyDown(int x)
    {
        if(!f[x]) return ;
        f[x<<1]=f[x<<1|1]=1;
        data[x<<1]=data[x<<1|1]=sz[x<<1]=sz[x<<1|1]=0;
        f[x]=0;
    }
    void Updata(int x,int l,int r,int p,int d,int num)
    {
        data[x]+=d;sz[x]+=num;
        if(l==r) return ;
        int mid=(l+r)>>1;
        LazyDown(x);
        if(p<=mid) Updata(x<<1,l,mid,p,d,num);
        else Updata(x<<1|1,mid+1,r,p,d,num);
        FoundData(x);
    }
    void Sectiondin(int x)
    {
        data[x]=sz[x]=0;f[x]=1;
    }
    int SectionQuery(int x,int l,int r,int sum)
    {
        if(sum<=0) return 0;
        if(l==r) return data[x];
        int mid=(l+r)>>1,ans=0;
        LazyDown(x);
        if(sz[x<<1]<=sum)
        {
            ans+=data[x<<1];
            ans+=SectionQuery(x<<1|1,mid+1,r,sum-sz[x<<1]);
            return ans;
        }
        else return SectionQuery(x<<1,l,mid,sum);
    }
    void init(int x)
    {
        for(int i=0;i<bl[x].size();i++)
        {
            int col=bl[x][i],r=dr[x][i];
            if(!t[col]) Updata(1,1,m,r,1,0),t[col]=r;
            else if(t[col]>r)
            {
                Updata(1,1,m,t[col],-1,0);
                Updata(1,1,m,r,1,0);
                t[col]=r;
            }
            Updata(1,1,m,r,0,1);
        }
    }
    void dinit(int x)
    {
        Sectiondin(1);
        for(int i=0;i<bl[x].size();i++) t[bl[x][i]]=0;
    }
}zt;
void insert(int x,int y)
{
    for(int i=0;i<bl[y].size();i++)
    {
        bl[x].push_back(bl[y][i]);
        dr[x].push_back(dr[y][i]);
    }
    bl[y].clear();dr[y].clear();
}
void dfs1(int x,int fa)
{
    sz[x]=bl[x].size();
    for(int i=first[x];i;i=eage[i].nxt)
        if(eage[i].t!=fa)
        {
            dfs1(eage[i].t,x);
            sz[x]+=sz[eage[i].t];
            if(sz[son[x]]<sz[eage[i].t]) son[x]=eage[i].t;
        }
}
void dfs(int x,int fa)
{
    for(int i=first[x];i;i=eage[i].nxt)
        if(eage[i].t!=fa&&eage[i].t!=son[x])
        {
            dfs(eage[i].t,x);
            zt.dinit(eage[i].t);
        }
    if(son[x]) dfs(son[x],x);
    zt.init(x);
    for(int i=first[x];i;i=eage[i].nxt)
        if(eage[i].t!=fa&&eage[i].t!=son[x])
            zt.init(eage[i].t);
ans[x]=zt.SectionQuery(1,1,m,a[x]);
    if(son[x])
    {
        insert(son[x],x);
        swap(bl[son[x]],bl[x]);
        swap(dr[son[x]],dr[x]);
        for(int i=first[x];i;i=eage[i].nxt)
            if(eage[i].t!=fa)
                insert(x,eage[i].t);
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        if(mp[y]==0) mp[y]=++cnt,y=cnt;
        else y=mp[y];
        bl[x].push_back(y);
        dr[x].push_back(i);
    }
    sz[0]=-1;
    dfs1(1,0);
    dfs(1,0);
    scanf("%d",&q);
    while(q--)
    {
        scanf("%d",&x);
        printf("%d\n",ans[x]);
    }
    return 0;
}
ac

相关文章:

  • 2021-12-14
  • 2021-09-01
  • 2021-12-06
  • 2021-05-02
  • 2021-07-04
  • 2022-01-29
  • 2022-12-23
  • 2022-12-23
猜你喜欢
  • 2021-09-08
  • 2022-12-23
  • 2022-03-01
  • 2021-11-19
  • 2021-10-22
  • 2022-03-01
  • 2022-12-23
相关资源
相似解决方案