牛客CSP-S提高组赛前集训营2 赛后总结

比赛链接

A.服务器需求

维护每天需要的服务器数量的全局最大值(记为\(Max\))总和(记为\(sum\)),那么答案为:

\[max(Max,\lceil\dfrac{sum}{m}\rceil) \]

证明略.

由于只有单点修改,可用一个 multiset 维护全局最大值和总和,当然,线段树也是可以的.

不过有一个易错点,在使用 multiset 的\(\text{.erase()}\)函数时,

  • 如果写\(\text{s.erase(5)}\),传入一个值,会删除所有的 5 .
  • 如果写\(\text{s.erase(find(5))}\),传入一个迭代器,只会删除 1 个 5.
#include<bits/stdc++.h>
const int SIZE=400005;
#define LL long long
int n,q,p;
LL m,x[SIZE],C,sum;
std::multiset<LL>s;
 
LL F()
{
    LL R=sum/m;
    if(sum%m>0)
        ++R;
    if(R<*--s.end())
        R=*--s.end();
    return R;  
}
 
int main()
{
    scanf("%d%lld%d",&n,&m,&q);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&x[i]);
        sum+=x[i];
        s.insert(x[i]);
    }
    printf("%lld\n",F());
    while(q--)
    {
        scanf("%d%lld",&p,&C);
        sum-=x[p];
        s.erase(s.find(x[p]));
        x[p]=C;
        s.insert(C);
        sum+=C;
        printf("%lld\n",F());
    }
    return 0;
}

B.沙漠点列

Tarjan 求割边和点双联通分量.

按贪心的思路,首先删除割边,每删除一条割边就会新出现一个连通块.

如果割边删完了,则删除点双联通分量中的边,容易发现,如果删除了一个点双联通分量中的\(j(j>0)\)条边,那么新出现\(j-1\)个连通块.

也就是说,删完一个点双联通分量,需要删除比新产生连通块数多 1 的边数,会浪费掉 1 次删边机会,我们想让这样的浪费尽可能少,所以贪心地从大的点双联通分量开始删起.

开始的时候我以为一个连通块就是一个环,也就是一个点双联通分量,然后才发现不是这样的😫.现场预习了怎么用 Tarjan 求点双连通分量后,才写出了正解.

Tarjan 求点双联通分量的方法:如果\(DFN[u]\le Low[v]\),则\(v\)到栈顶的所有点和\(u\)一起组成一个点双连通分量.

#include<bits/stdc++.h>
const int SIZE=4000005;
 
int In()
{
    char ch=getchar();
    int x=0;
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
    return x;
}
 
int n,m,k,head[SIZE],nex[SIZE],to[SIZE],Tot=1;
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;
}
 
std::priority_queue<int>q;
int DFN[SIZE],Low[SIZE],Sta[SIZE],Top,Tim,Ans;
bool mk[SIZE],Visited[SIZE];
void Tarjan(int u,int Fa)
{
    DFN[u]=Low[u]=++Tim;
    Sta[++Top]=u;
    for(int i=head[u];i;i=nex[i])
    {
        int v=to[i];
        if((Fa>>1)==(i>>1))continue;
        if(!DFN[v])
        {
            Tarjan(v,i);   
            Low[u]=std::min(Low[u],Low[v]);
            if(DFN[u]<Low[v])
                mk[i>>1]=1;
            if(DFN[u]<=Low[v])
            {
                int Siz=1,Tem;
                do{++Siz;}while(Sta[Top--]!=v);
                q.push(Siz);
            }
        }
        else
            Low[u]=std::min(Low[u],DFN[v]);
    }  
}
 
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;i++)
        Link(In(),In());
    for(int i=1;i<=n;i++)
        if(!DFN[i])
        {
            Tarjan(i,0);
            ++Ans;
        }
    int Cut_Edge=0;
    for(int i=2;i<=Tot;i+=2)
        if(mk[i>>1])
            ++Cut_Edge;
    if(k<=Cut_Edge)
        Ans+=k;
    else
    {
        k-=Cut_Edge;
        Ans+=Cut_Edge;
        while(q.size())
        {
            int Tem=q.top();
            q.pop();
            if(k>Tem)
            {
                k-=Tem;
                Ans+=Tem-1;                
            }
            else
            {
                Ans+=k-1;
                break;
            }
        }              
    }
    printf("%d",Ans);
    return 0;
}

C.维护序列

毒瘤

posted @ 2019-10-31 22:41  TaylorSwift13  阅读(155)  评论(0编辑  收藏  举报