题解:P12148 【MX-X11-T2】「蓬莱人形 Round 1」所以我放弃了音乐

赛时脑抽,艰难过掉。

题目大意

给定平面直角坐标系中的 \(n\) 个点,每次选择一个点,依次检查 \((x+1,y+1),(x+1,y),(x+1,y-1)\) 并跳到这个点继续删除直到不能删为止,记为一次操作,求最小的操作次数。

70 pts

一个经典的网络流问题,直接暴力建图跑最大流即可。

100 pts

特殊性质 A 启发我们按照不同的 \(x\) 分开处理。故开 \(10^6\)set 维护每个 \(x=i\) 时的所有 \(y\) 值。不难发现最终的答案是选择的不同路径的起点数量,我们要最小化这个东西。

接下来我们运用类似归纳法的思想得到贪心策略。

假设我们当前处理到了 \(x=i\) 的所有点,且这些点的贡献已经累计到答案中,那么 \(x=i\) 的这些点,无论是作为一条删除路径的起点或者是一条路径中的点,我们要为他们匹配一个 \(x=i+1\) 的后继,这样就能使 \(x=i+1\) 的点产生的贡献最小(因为能选择更少的起点)。

贪心策略:对于 \(x=i\) 的点 \(y\) 值从大到小处理,先匹配 \((x+1,y+1)\),再匹配 \((x+1,y)\),最后匹配 \((x+1,y-1)\)

正确性:感性理解,如果不优先匹配 \(y\) 更大的那么对于后面的点就更不可能匹配到了,而 \(y\) 更小的还可以交给其他点处理,故不按照 \((x+1,y+1),(x+1,y),(x+1,y-1)\) 的顺序一定不优,满足贪心的最优子结构和贪心选择性的性质。

时间复杂度 \(\Theta(n \log n)\)

代码

使用大量 stl,常数极大。

#include <bits/stdc++.h>
#define vint vector<int>
#define ll long long
#define pii pair<int,int>
#define all(x) x.begin(),x.end()
#define ull unsigned long long
#define uint unsigned int
#define rg register
#define il inline
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define sqr(x) ((x)*(x))
using namespace std;
const int INF=0x3f3f3f3f;
inline int read()
{
    int w=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        w=(w<<1)+(w<<3)+(ch^48);
        ch=getchar();
    }
    return w*f;
}
inline void write(int x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9) write(x/10);
    putchar(x%10+'0');
}

const int N=1e6+10;
int n;
int x[N],y[N];
set<int,greater<int>> pos[N],tmp[N];
map<pii,int> to;
bool used[N];
int ans,maxx;
int maxs[N];
map<int,int> posx[N];
int main()
{
    #ifndef ONLINE_JUDGE
    //freopen("in.txt","r",stdin);
    #endif
    cin>>n;
    rep(i,1,n) x[i]=read(),y[i]=read(),pos[x[i]].insert(y[i]),maxx=max(maxx,x[i]),to[make_pair(x[i],y[i])]=i;
    ans=pos[1].size();
    rep(i,1,maxx-1)
    {
        ans+=pos[i+1].size();
        
        for(auto it=pos[i].begin();it!=pos[i].end();++it)
        {
            int x=i,y=*it;
            if(to[make_pair(x+1,y+1)]&&!used[to[make_pair(x+1,y+1)]])
            {
                --ans,used[to[make_pair(x+1,y+1)]]=1;
                continue;
            }
            if(to[make_pair(x+1,y)]&&!used[to[make_pair(x+1,y)]])
            {
                --ans,used[to[make_pair(x+1,y)]]=1;
                continue;
            } 
            if(to[make_pair(x+1,y-1)]&&!used[to[make_pair(x+1,y-1)]])
            {
                --ans,used[to[make_pair(x+1,y-1)]]=1;
                continue;
            } 
        }
    }
    cout<<ans<<endl;
    return 0;
}
posted @ 2025-04-22 10:09  vanueber  阅读(23)  评论(0)    收藏  举报