祭最后的亡魂

这可能是我写的最后一篇博客了....

12号了,最后一场模拟赛也落下了帷幕,终于结束了,而下一次再战就是真正的战场了...

谨以此篇祭我最后的模拟赛与我的OI生涯...

我们先看第二题吧:

考场暴毙的题...

记得以前做过类似的题,不过不敢联系他们...

其实这类题我都是好迷的,将具体的问题抽象化,不是我擅长的领域.

我们简化题意:求所有的关键点两两匹配的最大权值。上一次做的是最小值,而且所有的点都是关键题....

我们直接顺着上一次的思路想,如果求最小值,那我们可以让关键点在子树内匹配,因为出来子树要比子树内坏很多,所以我们尽可能的让点在子树内搞,实在不行才出来,很显然,出来子树一定要经过x和才能到x的父亲另找人匹配...

就像这样以u为根的子树内的关键点必须经过u到v这条边才能符合要求,这样总的对答案的贡献就是点数*1...

而这道题则要求最大,我们可以很容易的想到每个点都出来子树,这样带来的价值更大...

但我们又要考虑,如果子树外的点小于子树内的点,我们就不能让所有的点都出来,因为出来后没法匹配了,所以我们只能让部分点在子树内匹配,之后让剩下的点出来子树匹配,显然,为了让答案最大,我们让子树外的点的数量都出来匹配..

剩下的就没了,代码很简单:

#include<bits/stdc++.h>
#define ll long long
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
#define pb(a) push_back(a)
using namespace std;
const int N=100010;
ll n,m,id,f[N],ans,link[N],tot,k;
bool vis[N];
struct bian{int y,next;}a[N*2]; 
inline int read()
{
    int x=0,ff=1;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*ff;
}
inline void add(int x,int y)
{
    a[++tot].y=y;
    a[tot].next=link[x];
    link[x]=tot;
}
inline void dfs(int x,int fa)
{
    if(vis[x]) f[x]++;
    for(register int i=link[x];i;i=a[i].next)
    {
        int y=a[i].y;
        if(y==fa) continue;
        dfs(y,x);
        f[x]+=f[y];
    }
    if(f[x]<=k) ans+=f[x];
    else     ans+=2*k-f[x];
}
int main()
{
    freopen("1.in","r",stdin);
    n=read();k=read();id=read();
    for(register int i=1,x;i<=2*k;++i) x=read(),vis[x]=1;
    for(register int i=1;i<n;++i)
    {
        int x=read(),y=read();
        add(x,y);add(y,x);
    }
    dfs(1,0);
    printf("%d",ans);
    return 0;
}

至于这类题为什么这么写,不是很懂...但仔细想想:

我们暴力求的是将每一个路径单独求解出来,然后将路径和相加,但这些思想显然没有那么具体化,他们只是在考虑每条边对答案的贡献,即这条边(如:上图中u到v)被多少个点经过,这样就不用了费时费力的枚举了...

然后分析点匹配的最优性,考虑什时候让他们经过那条边最优,这样就行了吧....

之后看第二题吧:

这个题超有信心的写了个二分+最大匹配,然后,然后就MLE了.....(呜呜呜)

这是最后一场模拟赛了,竟然还犯这种错误,这真的给我敲响了警钟...不要太掉以轻心,否则多么失望也只能接受..

都到最后还会犯MLE这种错误,那么惩罚就是但从现在开始把每一道题都当做CSP,该注意的都要注意,不能在随随便便交题了....

好了,回归这道题:

考场上注意到这应该是是个贪心,但由于我贪心的能力并不强,果断放弃,当看到每个怪只能打一次后,果断想起二分图的匹配,于是就草草的打了二分图的最大匹配去干第二题...

但除了空间问题之外,这种做法好像还会T,不知道为什么....

考后,看了某一位强者的check,使用贪心写的,真的好妙...

当给定一个答案时,我们思考如何判定,我们检查答案一定是先讨论最坏的情况,把更好的策略留给后面的人..

首先把人和怪兽坐标sort(不要问我为什么,因为这是在一维上),之后我么考虑答案的构成(人到怪兽的距离)+(怪兽到s的距离),那我们将人看成被动,兽看成主动的,排过序后针对每一个兽,他到s的距离是一定的,之后考虑到人的距离,我们会发现

只有将人和兽的坐标一一对应时才最优,否则必会造成更大的代价...

如图,圆圈代表人,三角代表怪兽,对于第三个图,第三个怪兽如果选择第一个人那么第一个怪兽无论选择2或3都会比原来选1,导致整体不是最优的,所以我们check的时候,只用用两个指针i和j扫一遍即可.

#include<bits/stdc++.h>
#define ll long long
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
#define pb(a) push_back(a)
using namespace std;
const int N=5100,qwq=1e9;
int p[N],q[N],n,m,s;
inline int read()
{
    int x=0,ff=1;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*ff;
}
inline bool check(int x)
{
    int now=1;
    for(register int i=1;i<=n;++i)
    {
        while(now<=m&&abs(p[i]-q[now])+abs(q[now]-s)>x) now++;
        if(now>m) return 0;
        now++;
    }
    return 1;
}
int main()
{
    freopen("kill.in","r",stdin);
    freopen("kill.out","w",stdout); 
    n=read();m=read();s=read();
    for(register int i=1;i<=n;++i) p[i]=read();
    for(register int i=1;i<=m;++i) q[i]=read();
    sort(p+1,p+n+1);sort(q+1,q+m+1);
    int l=1,r=qwq*2;
    while(l<r)
    {
        int mid=(l+r)>>1;
        if(check(mid)) r=mid;
        else           l=mid+1;
    } 
    printf("%d",l);
    return 0;
}

 第三题正解是树剖,所以这里只讲暴力(不要问我为什么...)

有30分是n==m,显然树是个基环树,那对于不是环的边,一定在最小生成树上,则它权值可以随便变,若在环上,我们则考虑它与最大值的关系,因为它要在树上,必须删掉一个边,也就是说至少有一根边权值比他大.

那我们就找一下环的最大值和次大值,对于环上的边,如果是最大值,那最大为次大值-1,如果是次大值,则可为最大值-1...

只后有30是所有的边权为1,那我们用割边判断一下是不是必须的即可...

#include<bits/stdc++.h>
#define ll long long
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
#define pb(a) push_back(a)
using namespace std;
const int N=70010,M=100010;
int f[N],dfn[N],n,m,id,link[N],tot=1,p,o,mx,mxx;
int low[N];
bool vis[M*2];
struct bian{int y,next,v;}a[M*2]; 
inline int read()
{
    int x=0,ff=1;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*ff;
}
inline void add(int x,int y,int v)
{
    a[++tot].y=y;
    a[tot].v=v;
    a[tot].next=link[x];
    link[x]=tot;
}
inline void dfs(int x)
{
    dfn[x]=++p;
    for(register int i=link[x];i;i=a[i].next)
    {
        int y=a[i].y;
        if(y==o) continue;
        if(!dfn[y]) f[y]=x,dfs(y);
        else if(y!=f[x])
        {
            int now=x;o=x;
            vis[i]=1;mx=a[i].v;
            while(now!=y) 
            {
                for(int j=link[now];j;j=a[j].next)
                {
                    int v=a[j].y;
                    if(v==f[now])
                    {
                        vis[j]=1;
                        if(a[j].v>mx) mxx=mx,mx=a[j].v;
                        else if(a[j].v==mx) mxx=mx;
                        else if(a[j].v<mx&&a[j].v>mxx) mxx=a[j].v; 
                        now=v;break;
                    }
                }
            }
        } 
    } 
}
inline void solve1()
{
    dfs(1);
    for(register int i=2;i<=2*m+1;i+=2)
    {
        if(!vis[i]&&!vis[i^1]) printf("-1 ");
        else 
        {
            if(a[i].v==mx) printf("%d ",mxx-1);
            else           printf("%d ",mx-1);
        }
    }
}
inline void tarjan(int x,int fa)
{
    dfn[x]=low[x]=++p;
    for(register int i=link[x];i;i=a[i].next)
    {
        int y=a[i].y;
        if(!dfn[y])
        {
            tarjan(y,x);
            low[x]=min(low[x],low[y]);
            if(low[y]>dfn[x]) vis[i]=vis[i^1]=1;
        }
        else if(y!=fa) low[x]=min(low[x],dfn[y]);
    }
}
inline void solve2()
{
    for(register int i=1;i<=n;++i) if(!dfn[i]) tarjan(i,0);
    for(register int i=2;i<=tot;i+=2)
    {
        if(!vis[i]) printf("0 ");
        else        printf("-1 ");
    }
}
int main()
{
    freopen("weight.in","r",stdin);
    freopen("weight.out","w",stdout);
    n=read();m=read();id=read();
    for(register int i=1;i<=m;++i)
    {
        int x=read(),y=read(),v=read();
        add(x,y,v);add(y,x,v);
    }
    if(n==m) solve1();
    else if(id) solve2();
    return 0;
}

至于调了一下午的代码,究其原因竟然是数组没开够,下次还得谨慎,因为这会影响你对算法的信心...

该开始数组还要考虑周到...

这次模拟赛是我订正最认真的一次吧,或许是最后一次了,且行且珍惜...

至于心态,我觉得比知识更重要,在这里,再安慰自己一下:

不要管别人怎么奶你的,你不是为他们比赛的,做出自己最大的努力.

尽全力,不为满足别人的期望与自己的虚荣,而是对人生的负责...

曾与别人交谈:你没得省一那就真亏了,亏了吗?难道我坚持一年只为了一纸证书???

相比于此,我觉得园神说得对:你别考虑成绩,咱们学知识知识多了一门技术,这对我们怎么来说都是有益的.要不然你整天想来想去,压力得多大呀!

这里真的真挚的感谢她,那时候我真的给自己施了太大的压力,仿佛每天真的喘不过气,大山般的压力真切的感受到了...

经她一点,仿佛重见天日,对呀,没得省一又怎么了,没拿奖我就白学编程了吗?编程带给我的难道就只能是一纸证书吗?

初学算法的艰辛,说自己不学状压,可还是硬生生的做了几道模板..

无穷无尽的debug,让我在坚韧中认真起来,让我知道了什么才是真正的永不放弃...

理性思维逐渐让我思考的更深,逐渐对人生更加合理的规划...

无论结果如何,我都享受过程了,这一路走来,我无悔!

不论结果,吾以吾血祭祭吾最后的亡魂!!!

OI生涯,青春无悔!

原文地址:https://www.cnblogs.com/gcfer/p/11843439.html