2019/9/17 校内练习赛 解题报告

比赛详情

本次练习赛已收录至2019/9/22 本周总结

tree

给定一棵无根树,求使得所有节点深度和最大的根节点.

思路

考虑先令\(1\)为根,\(tot\)为此时所有节点深度和,预处理\(size(x)\)表示\(x\)子树的大小.设\(u\)为\(1\)的某个儿子,显然从\(1\)走到\(u\)时,有:

\[tot=tot+(n-size[u])-size[u]\]

将结论推广到所有节点,扫描整棵树得到答案,时间复杂度\(O(n)\).

代码

#include<bits/stdc++.h>
const int SIZE=2000005;

int n,head[SIZE],nex[SIZE],to[SIZE],Tot;
int Dep[SIZE],Siz[SIZE],D[SIZE];
long long Ans;
int Ans_p;

void Link(int u,int v)
{
    nex[++Tot]=head[u];head[u]=Tot;to[Tot]=v;
    nex[++Tot]=head[v];head[v]=Tot;to[Tot]=u;
}

void DFS(int u,int F)
{
    Siz[u]=1;
    for(int i=head[u];i;i=nex[i])
    {
        int v=to[i];
        if(v==F)continue;
        Dep[v]=Dep[u]+1;
        DFS(v,u);
        Siz[u]+=Siz[v];
        D[u]+=D[v]+Siz[v];
    }
}

void DFS2(int u,int F,long long nowD)
{
    if(Ans<nowD)
    {
        Ans=nowD;
        Ans_p=u;
    }
    if(Ans==nowD)
    {
        Ans_p=std::min(Ans_p,u);
    }
    for(int i=head[u];i;i=nex[i])
    {
        int v=to[i];
        if(v==F)continue;
        DFS2(v,u,nowD+(n-Siz[v])-Siz[v]);
    }
}

int main()
{
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    scanf("%d",&n);
    int u,v;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        Link(u,v);
    }
    Dep[1]=0;
    DFS(1,0);
    DFS2(1,0,D[1]);
    printf("%d",Ans_p);
    return 0;
}

safe

思路

预处理\(L[i]\)为\([1,i]\)最大子段和,\(R[i]\)为\([i+1,n]\)最大子段和.

答案即为:

\[max(L[i]+R[i])\ i\in[1,n-1]\]

代码

#include<bits/stdc++.h>
const int SIZE=70000;
#define LL long long

int n;
LL x[SIZE],L[SIZE],R[SIZE],DP[SIZE],Ans=-0x3F3F3F3F3F3F3F3F;

int main()
{
    freopen("safe.in","r",stdin);
    freopen("safe.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",&x[i]);
    memset(L,-0x3F,sizeof(L));
    memset(R,-0x3F,sizeof(R));
    memset(DP,-0x3F,sizeof(DP));
    for(int i=1;i<=n;i++)
    {
        DP[i]=std::max(DP[i-1]+x[i],x[i]);
        L[i]=std::max(L[i-1],DP[i]);
    }
    memset(DP,-0x3F,sizeof(DP));
    for(int i=n;i;i--)
    {
        DP[i]=std::max(DP[i+1]+x[i],x[i]);
        R[i]=std::max(R[i+1],DP[i]);
    }
    for(int i=2;i<=n;i++)
    {
        Ans=std::max(Ans,L[i-1]+R[i]);
    }
    printf("%lld",Ans);
    return 0;
}

shoot

吐槽

本以为是没有上司的舞会的基环树版,然后假掉了.

本以为是神仙结论题,然后就没有然后了.

最后发现是分情况贪心乱搞,对拍了\(n\)次终于(似乎)考虑了所有情况.

思路

对每一个连通块分开讨论.

最大值

能自黑就自黑并删掉自黑的点.

剩下的点中,若只剩环,则可以黑掉(环长\(-1\))个点.

若不是只剩环,入度不为\(0\)的点,都能被黑.

最小值

拓扑排序处理不在环上的点,该黑就黑,否则不黑.

破环成链,模拟处理环上的点(隔一个黑一个).

代码

#include<bits/stdc++.h>
const int SIZE=2000005;

int n,x[SIZE],Ans1,Ans2;
int head[SIZE],nex[SIZE],to[SIZE],Deg1[SIZE],Deg2[SIZE],Tot,now[SIZE],Cnt;
bool edge[SIZE],Visited[SIZE],Black[SIZE],Loop[SIZE];
int Loop_Tot;

void Link(int u,int v)
{
    nex[++Tot]=head[u];head[u]=Tot;to[Tot]=v;edge[Tot]=0;
    nex[++Tot]=head[v];head[v]=Tot;to[Tot]=u;edge[Tot]=1;
}

void mk(int u)
{
    if(Visited[u])return;
    //printf("DFS %d\n",u);
    Visited[u]=1;
    now[++Cnt]=u;
    for(int i=head[u];i;i=nex[i])
    {
        int v=to[i];
        mk(v);
    }
}

std::queue<int>q;
void Topo(int o)
{
    //puts("*********************");
    while(q.size())
    {
        int u=q.front();
        q.pop();
        //printf("pop %d\n",u);
        if(!Black[u]&&!Black[x[u]])
        {
            Black[x[u]]=1;
            //printf("Black %d\n",x[u]);
            ++Ans1;
        }
        --Deg2[x[u]];
        if(Deg2[x[u]]==0)
        {
            q.push(x[u]);
            //printf("push %d\n",x[u]);
        }

    }
    //puts("*********************");
    int Flag=0;
    bool Finished=0;
    for(int i=1;i<=Cnt;i++)
    {
        if(Deg2[now[i]])
        {
            Loop[now[i]]=1;
            o=now[i];
            //printf("Loop %d\n",now[i]);
            ++Loop_Tot;
        }
    }

    for(int i=o;i;i=x[i])
    {
        if(i==o)
        {
            if(Finished==0)Finished=1;
            else break;
        }
        if(Black[i]==1)
        {
            Flag=i;
            break;
        }
    }
    bool now=1;
    if(Flag==0)
    {
        Flag=o;
        now=0;
    }
    /*else
    {*/

        Finished=0;
        Flag=x[Flag];
        for(int i=Flag;i;i=x[i])
        {
            if(i==Flag)
            {
                if(Finished==0)Finished=1;
                else break;
            }
            if(Black[i]==1)
            {
                now=1;
                continue;
            }
            else
            {
                now^=1;
                if(now)
                {
                    Black[i]=1;
                    //printf("Black %d\n",i);
                    ++Ans1;
                }
            }
        }
    //}
}
bool PD[SIZE];

int main()
{
    freopen("shoot.in","r",stdin);
    freopen("shoot.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x[i]);
        Deg1[x[i]]++;
        Deg2[x[i]]++;
        Link(i,x[i]);
    }
    for(int i=1;i<=n;i++)
    {
        /*if(x[i]==i)//×??·
        {
            ++Ans1;
            ++Ans2;
            continue;
        }*/
        if(!Visited[i])
        {
            //puts("");
            Cnt=0;
            Loop_Tot=0;
            mk(i);
            int Ha=Cnt;
            if(Cnt==1)
            {
                ++Ans1;
                ++Ans2;
                continue;
            }
            else
            {
                for(int k=1;k<=Cnt;k++)
                {
                    //printf("%d ",now[k]);
                    //puts("");
                    if(Deg1[now[k]]==0)
                    {
                        ++Ans2;
                        //puts("++Ans2");
                        q.push(now[k]);
                        //printf("push %d\n",now[k]);
                        PD[now[k]]=1;
                        --Ha;
                    }
                }
                Topo(i);
                for(int k=1;k<=Cnt;k++)
                {
                    if(PD[now[k]]==1)
                    {
                        --Deg1[x[now[k]]];
                        //printf("--Deg %d owing to %d\n",x[now[k]],now[k]);
                    }
                }
                //int Ha=0;
                for(int k=1;k<=Cnt;k++)
                {
                    if(Deg1[now[k]])
                    {
                        //++Ha;
                        //printf("still %d\n",now[k]);
                        ++Ans2;
                        //puts("++Ans2");
                    }
                }
                //printf("Ha=%d Loop=%d\n",Ha,Loop_Tot);
                if(Ha==Loop_Tot&&Ha!=1)
                {
                    --Ans2;
                    //puts("--Ans2");
                }
            }
        }
    }
    printf("%d %d",Ans1,Ans2);
    return 0;
}

附:暴力代码

#include<bits/stdc++.h>
const int SIZE=1000005;

int n,x[SIZE],P[SIZE],Deg[SIZE],Ans1=10,Ans2;
bool Visited[SIZE],mk[10];

void DFS2(int step,int Ans)
{
    if(step==n+1)
    {
        Ans1=std::min(Ans1,Ans);
        Ans2=std::max(Ans2,Ans);
        return;
    }
    int i=P[step];
    if(mk[i])DFS2(step+1,Ans);
    else
    {
        if(Deg[i]==0)
        {
            //自黑
            if(mk[i]==0)
            {
                mk[i]=1;
                DFS2(step+1,Ans+1);
                mk[i]=0;
            }

            if(mk[i]==1)
            {
                DFS2(step+1,Ans);
            }
            //黑别人
            if(mk[x[i]]==0)
            {
                mk[x[i]]=1;
                DFS2(step+1,Ans+1);
                mk[x[i]]=0;
            }

            if(mk[x[i]]==1)
            {
                DFS2(step+1,Ans);
            }
        }
        else
        {
            if(mk[x[i]]==0)
            {
                mk[x[i]]=1;
                DFS2(step+1,Ans+1);
                mk[x[i]]=0;
            }

            if(mk[x[i]]==1)
            {
                DFS2(step+1,Ans);
            }
        }
    }

}

void DFS(int step)
{
    if(step==n+1)
    {
        /*for(int i=1;i<=8;i++)
            printf("%d",P[i]);
        puts("");*/
        memset(mk,0,sizeof(mk));
        DFS2(1,0);
    }
    for(int i=1;i<=n;i++)
    {
        if(!Visited[i])
        {
            Visited[i]=1;
            P[step]=i;
            DFS(step+1);
            Visited[i]=0;
        }
    }
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x[i]);
        Deg[x[i]]++;
    }
    DFS(1);
    printf("%d %d",Ans1,Ans2);
    return 0;
}

原文地址:https://www.cnblogs.com/TaylorSwift13/p/11558809.html

时间: 2024-11-06 07:18:44

2019/9/17 校内练习赛 解题报告的相关文章

2019模拟赛09场解题报告

目录 2019模拟赛09场解题报告 目录la~~ 题一:瞬间移动 题二:食物订购 题三:马蹄印 题四:景观美化 2019模拟赛09场解题报告 标签(空格分隔): 解题报告 Forever_chen 2019.8.20 目录la~~ 题一:瞬间移动 [题面] 有一天,暮光闪闪突然对如何将一个整数序列a1,a2,...,an排序为一个不下降序列起了兴趣.身为一只年轻独角兽的她,只能进行一种叫做"单元转换"(unit shift)的操作.换句话说,她可以将序列的最后一个元素移动到它的起始位置

【2019/3/17】周进度报告

  第二周 所花时间 约7个小时 代码量 50多行 博客量 2篇 了解到的知识点 1.BigInterger的使用 2.Android生命周期 原文地址:https://www.cnblogs.com/limitCM/p/10548308.html

解题报告 smoj 2019初二创新班(2019.3.17)

目录 解题报告 smoj 2019初二创新班(2019.3.17) T1:找玩具 题目描述 题意转化 分析 代码 优化(代码复杂度) T2:闯关游戏 题目描述 分析 代码 T3:子数组有主元素 题目描述 分析 代码(\(O(nm\log n)\)) 优化 代码(\(O(nm)\)) 解题报告 smoj 2019初二创新班(2019.3.17) 时间:2019.3.21 T1:找玩具 题目描述 在游戏开始之前,游戏大师在房间的某些地方隐藏了N个玩具.玩具编号为1到N.您的任务是尽可能多地找到这些玩

2020-3-14 acm训练联盟周赛Preliminaries for Benelux Algorithm Programming Contest 2019 解题报告+补题报告

2020-3-15比赛解题报告+2020-3-8—2020-3-15的补题报告 2020-3-15比赛题解 训练联盟周赛Preliminaries for Benelux Algorithm Programming Contest 2019  A建筑(模拟) 耗时:3ms 244KB 建筑 你哥哥在最近的建筑问题突破大会上获得了一个奖项 并获得了千载难逢的重新设计城市中心的机会 他最喜欢的城市奈梅根.由于城市布局中最引人注目的部分是天际线, 你的兄弟已经开始为他想要北方和东方的天际线画一些想法

「csp校内训练 2019-10-24」解题报告

「csp校内训练 2019-10-24」解题报告 T1.猴猴吃苹果 \(Description\) 猴猴最喜欢在树上玩耍,一天猴猴又跳上了一棵树,这棵树有 \(N \ (N \leq 50000)\) 个苹果,每个苹果有一个编号,分别为 \(0\) ~ \(N - 1\) 它们之间由 \(N-1\) 个树枝相连,猴猴可以从树枝的一端爬到树枝的另一端,所以猴猴可以从任意一个苹果的位置出发爬到任意猴猴想去的苹果的位置. 猴猴开始在编号为 \(K \ (K < N)\) 的苹果的位置,并且把这个苹果吃

「csp校内训练 2019-10-30」解题报告

「csp校内训练 2019-10-30」解题报告 T1.树 题目链接(逃) \(Description\): 现在有一棵树,共 \(N\) 个节点. 规定:根节点为 \(1\) 号节点,且每个节点有一个点权. 现在,有 \(M\) 个操作需要在树上完成,每次操作为下列三种之一: \(1 \ x \ a\):操作 \(1\),将节点 \(x\) 点权增加 \(a\). \(2 \ x \ a\):操作 \(2\),将以节点 \(x\) 为根的子树中所有点的权值增加 \(a\). \(3 \ x\)

解题报告-2019.12.16

解题报告-2019.12 题目:6-3[拓展编程题_课后练习3][P215 习题8-三-4] 报数 (20分) 题目详情: 报数游戏是这样的:有n个人围成一圈,按顺序从1到n编好号.从第一个人开始报数,报到m(<n)的人退出圈子:下一个人从1开始报数,报到m的人退出圈子.如此下去,直到留下最后一个人. 本题要求编写函数,给出每个人的退出顺序编号. 函数接口定义:void CountOff( int n, int m, int out[] ); 其中n是初始人数:m是游戏规定的退出位次(保证为小于

2016 第七届蓝桥杯 c/c++ B组省赛真题及解题报告

2016 第七届蓝桥杯 c/c++ B组省赛真题及解题报告 勘误1:第6题第4个 if最后一个条件粗心写错了,答案应为1580. 条件应为abs(a[3]-a[7])!=1,宝宝心理苦啊.!感谢zzh童鞋的提醒. 勘误2:第7题在推断连通的时候条件写错了,后两个if条件中是应该是<=12 落了一个等于号.正确答案应为116. 1.煤球数目 有一堆煤球.堆成三角棱锥形.详细: 第一层放1个, 第二层3个(排列成三角形), 第三层6个(排列成三角形), 第四层10个(排列成三角形). -. 假设一共

Winter-2-STL-E Andy&#39;s First Dictionary 解题报告及测试数据

use stringstream Time Limit:3000MS     Memory Limit:0KB Description Andy, 8, has a dream - he wants to produce his very own dictionary. This is not an easy task for him, as the number of words that he knows is, well, not quite enough. Instead of thin