2025多校冲刺省选模拟赛8

2025多校冲刺省选模拟赛8

T1 A. 波斯菊 10pts/10pts

  • 每个连通块内以关键点为起点的最长路径 di 相互独立,有 2n2ki=1kdk 即为所求。

  • 观察到可以直接钦定每个关键点距离其最远的点的是哪个点,这样的话就只需要任意两个路径不相交就能保证一定存在一种划分方案合法。

  • fx,0/1/2 分别表示以 x 为根的子树内只有一个起点在往上走/只有一个终点在往上走(不一定要被匹配到)/没有点在往上走的答案。

  • 转移时考虑从两棵子树内的节点转移而来,特殊处理关键点处的转移。

    点击查看代码
    struct node
    {
        ll nxt,to;
    }e[1000010];
    ll head[500010],vis[500010],f[3][500010],cnt=0;
    void add(ll u,ll v) 
    {
        cnt++;
        e[cnt].nxt=head[u];
        e[cnt].to=v;
        head[u]=cnt;
    }
    void dfs(ll x,ll fa)
    {
        ll maxx,sum=0,u=-0x3f3f3f3f3f3f3f3f,v=-0x3f3f3f3f3f3f3f3f;
        for(ll i=head[x];i!=0;i=e[i].nxt)
        {
            if(e[i].to!=fa)
            {
                dfs(e[i].to,x);
                sum+=max(f[1][e[i].to],f[2][e[i].to]);
            }
        }
        if(vis[x]==1)
        {
            f[1][x]=sum;
            for(ll i=head[x];i!=0;i=e[i].nxt)
            {
                if(e[i].to!=fa)
                {
                    f[0][x]=max(f[0][x],sum-max(f[1][e[i].to],f[2][e[i].to])+f[0][e[i].to]);
                    f[2][x]=max(f[2][x],sum-max(f[1][e[i].to],f[2][e[i].to])+f[0][e[i].to]);
                }
            } 
        }
        else
        {
            f[0][x]=1;
            f[1][x]=-0x3f3f3f3f3f3f3f3f;
            f[2][x]=sum;
            for(ll i=head[x];i!=0;i=e[i].nxt)
            {
                if(e[i].to!=fa)
                {
                    maxx=max(f[1][e[i].to],f[2][e[i].to]);
                    f[0][x]=max(f[0][x],sum-maxx+f[0][e[i].to]+1);
                    f[1][x]=max(f[1][x],sum-maxx+f[1][e[i].to]+1);
                    f[2][x]=max({f[2][x],sum-maxx+f[1][e[i].to]+1+u,sum-maxx+f[0][e[i].to]+1+v});
                    u=max(u,f[0][e[i].to]-maxx);  v=max(v,f[1][e[i].to]-maxx);
                }
            }
        }
    }
    int main()
    {
    #define Isaac
    #ifdef Isaac
        freopen("coreopsis.in","r",stdin);
        freopen("coreopsis.out","w",stdout);
    #endif
        ll n,k,x,u,v,i;
        cin>>n>>k;
        for(i=1;i<=k;i++)
        {
            cin>>x;
            vis[x]=1;
        }
        for(i=1;i<=n-1;i++)
        {
            cin>>u>>v;
            add(u,v);  add(v,u);
        }
        dfs(x,0);
        cout<<2*n-2*k-max({f[0][x],f[1][x],f[2][x]})<<endl;
        return 0;
    }
    

T2 B. 芦荀花 60pts/60pts

  • 连通块数 1 即为所求。

  • 部分分

    • 5060pts
      • 连通块数等于边数减点数,考虑差的来源是由于连通块顶部的点导致的。稍微卡卡常,时间复杂度为 O(nm)

        点击查看代码
        int fa[500010],dfn[500010],pos[500010],vis[500010],tot=0;
        pair<int,int>a[500010];
        vector<int>e[500010];
        void add(int u,int v)
        {
            e[u].push_back(v);
        }
        void dfs(int x,int father)
        {
            tot++;  dfn[x]=tot;  pos[tot]=x;
            fa[x]=father;
            for(int i=0;i<e[x].size();i++)
            {
                if(e[x][i]!=father)  dfs(e[x][i],x);
            }
        }
        int main()
        {
        #define Isaac
        #ifdef Isaac
            freopen("fructus.in","r",stdin);
            freopen("fructus.out","w",stdout);
        #endif
            int n,m,b,ans=0,u,v,i,j,k;
            scanf("%d%d%d",&n,&m,&b);
            for(i=1;i<=n-1;i++)
            {
                scanf("%d%d",&u,&v);
                add(u,v);  add(v,u);
            }
            for(i=1;i<=n;i++)  sort(e[i].begin(),e[i].end());
            dfs(1,0);
            for(;m>=1;m--)
            {
                scanf("%d",&k);   ans*=b;
                for(i=1;i<=k;i++)
                {
                    scanf("%d%d",&a[i].first,&a[i].second);
                    a[i].first^=ans;  a[i].second^=ans;
                    for(j=a[i].first;j<=a[i].second;j++)  vis[pos[j]]=1;
                }
                ans=-1;
                for(i=k;i>=1;i--)  
                {
                    for(j=a[i].second;j>=a[i].first;j--)  
                    {
                        vis[pos[j]]=0;
                        ans+=(vis[fa[pos[j]]]==0);
                    }
                }
                printf("%d\n",ans);
            }
            return 0;
        }
        
      • 若连通块内部已经连通是容易处理的,取 LCA 判断即可。按照 DSF 序记录叶子节点后划分成若干条链后时间复杂度瓶颈不在判断某个点是否被选中,即使优化也没有什么大的用处,时间复杂度为 O(mt) ,其中 t 为叶子个数。

        • 貌似把拆分成链转换成拆分成子树就可以通过 Subtest7,k=1 的测试点。
        点击查看代码
        int fa[500010],dfn[500010],vis[500010],tot=0;
        pair<int,int>a[500010];
        vector<int>e[500010],leaf;
        void add(int u,int v)
        {
            e[u].push_back(v);
        }
        void dfs(int x,int father)
        {
            int flag=0;
            tot++;  dfn[x]=tot;
            fa[tot]=dfn[father];
            for(int i=0;i<e[x].size();i++)
            {
                if(e[x][i]!=father)  
                {
                    flag=1;
                    dfs(e[x][i],x);
                }
            }
            if(flag==0)  leaf.push_back(dfn[x]);
        }
        int main()
        {
        #define Isaac
        #ifdef Isaac
            freopen("fructus.in","r",stdin);
            freopen("fructus.out","w",stdout);
        #endif
            int n,m,b,ans=0,tmp,u,v,i,j,k;
            scanf("%d%d%d",&n,&m,&b);
            for(i=1;i<=n-1;i++)
            {
                scanf("%d%d",&u,&v);
                add(u,v);  add(v,u);
            }
            for(i=1;i<=n;i++)  sort(e[i].begin(),e[i].end());
            dfs(1,0);
            for(;m>=1;m--)
            {
                scanf("%d",&tmp);  k=1;  ans*=b;
                for(i=1;i<=tmp;i++)
                {
                    scanf("%d%d",&a[i].first,&a[i].second);
                    a[i].first^=ans;  a[i].second^=ans;
                    for(j=a[i].first;j<=a[i].second;j++)  vis[j]=1;
                    if(i>=2)
                    {
                        if(a[i].first==a[k].second+1)  a[k].second=a[i].second;
                        else
                        {
                            k++;
                            a[k]=a[i];
                        }
                    }
                }
                ans=-1;
                u=lower_bound(leaf.begin(),leaf.end(),a[k].first)-leaf.begin();
                v=upper_bound(leaf.begin(),leaf.end(),a[k].second)-leaf.begin()-1;
                for(i=k;i>=1;i--)
                {
                    while(leaf[u-1]>=a[i].first&&u-1>=0)  u--;
                    while(leaf[v]>a[i].second&&v>=0)  v--;
                    if(u>v)  ans+=(vis[fa[a[i].first]]==0);
                    else 
                    {
                        for(j=u;j<=v;j++)
                        {
                            ans+=((vis[fa[(j==u)?a[i].first:(leaf[j-1]+1)]])==0);
                        }
                        if(a[i].second!=leaf[v])  ans+=(vis[fa[leaf[v]+1]]==0);
                    }
                    for(j=a[i].first;j<=a[i].second;j++)  vis[j]=0;
                }
                printf("%d\n",ans);
            }
            return 0;
        }
        
        
  • 正解

T3 C. 豆蔻花 0pts/0pts

总结

  • T1 读假题了,以为同时可以移动 k 个石子,最后的式子多一个连通块大小,直接假了。
  • 没想到 T2 主席树维护二维数点的暴力做法。
posted @   hzoi_Shadow  阅读(25)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
扩大
缩小
点击右上角即可分享
微信分享提示