祭最后的亡魂

扫码查看

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

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;
}
01-31 16:26
查看更多