• 祭最后的亡魂


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

    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生涯,青春无悔!

  • 相关阅读:
    利用VS的预生成事件获取SVN版本作为项目内部版本号
    静态构造函数到底执行了多少次?
    C#之自定义的implicit和explicit转换
    FineUI之使用SQL脚本从数据库表中生成相应的输入控件
    文件操作
    PHP中文件类型 文件属性 路径以及 文件相关的函数
    MySqli 中预处理类 stmt
    MySql 事务处理
    MySqli 执行多条SQL语句
    PHP与MySqli
  • 原文地址:https://www.cnblogs.com/gcfer/p/11843439.html
Copyright © 2020-2023  润新知