题解 P2403 [SDOI2010]所驼门王的宝藏

Posted colazcy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解 P2403 [SDOI2010]所驼门王的宝藏相关的知识,希望对你有一定的参考价值。

题目链接

Solution [SDOI2010]所驼门王的宝藏

题目大意:给定一个\(R\)\(C\)列的矩阵,有些方格有宝藏和传送门.你可以从任意方格进入,到达有宝藏的宫室时可以横行任意传送、纵行任意传送、八连块任意传送(视传送门类型而定),问最多可以获得多少宝藏

分析:看到什么"传送"这类字眼我们多半就会想到图论.于是乎我们\(1min\)就可以想出一个暴力算法:

每个有宝藏的点向它可以到达的所有有宝藏的点连边,\(Tarjan\)缩点后求\(DAG\)最长路

大概有\(40\)分了?

我们看看问题出在哪儿:每个点向它所有可以到达的点连边,但是如果所有的点都在同一行,而且都为"横天门"的话,那你建边就要\(O(n ^ 2)\)的复杂度,不可接受

sb的我:啊!这是区间连边!可以线段树优化建图!

教练:醒一醒!线段树建图空间复杂度\(n^2\)

言归正传,我们最后都是要缩点的,根本没必要对于每个点都暴力连边.我们可以对于每行、每列都设置一个虚拟点,从虚拟点向当前行(列)每个点连边.然后对于每个点,我们向其行(列)虚拟点连边,这样从每个点出发还是可以走到当前行(列)所有点,但是边数大大减少

对于八联通块?我们可以用\(hash\)把所有有宝藏的点的坐标存起来,然后暴力找找连边即可

于是成型算法

  • 行列设置虚拟点,向当前行(列)所有点连边
  • 横天门向他所在的这一行虚拟点连一条边
  • 纵寰门向他所在的这一列虚拟点连一条边
  • w自w由w门w暴力连边

代码:

#include <cstdio>
#include <cctype>
#include <vector>
#include <stack>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 1e5;
int n,r,c;
inline int read()
    int x = 0;char c = getchar();
    while(!isdigit(c))c = getchar();
    while(isdigit(c))x = x * 10 + c - '0',c = getchar();
    return x;

int head[3 * maxn],nxt[3 * maxn],to[3 * maxn];
vector<int> line[maxn],column[maxn];
inline void addedge(int from,int to)
    static int tot;
    ::to[++tot] = to;
    nxt[tot] = head[from];
    head[from] = tot;

namespace Tarjan//Tarjan板子
    int dfn[2 * maxn],low[2 * maxn],instk[2 * maxn],col[2 * maxn],siz[2 * maxn],dfs_tot,col_tot;
    stack<int> stk;
    void tarjan(int u)
        dfn[u] = low[u] = ++dfs_tot;
        stk.push(u),instk[u] = 1;
        for(int i = head[u];i;i = nxt[i])
            int v = to[i];
            if(!dfn[v])tarjan(v),low[u] = min(low[u],low[v]);
            else if(instk[v])low[u] = min(low[u],dfn[v]);
        
        if(low[u] == dfn[u])
            int t;++col_tot;
            do
                t = stk.top();stk.pop(),instk[t] = 0;
                col[t] = col_tot;
                siz[col_tot] += t <= n;//只有那些有宝藏的点(编号小于等于n)的点才对答案有贡献
            while(t != u);
        
    
using namespace Tarjan;
namespace Solve//dp
    int head[2 * maxn],nxt[2 * maxn],to[2 * maxn];
    inline void addedge(int from,int to)
        static int tot;
        Solve::to[++tot] = to;
        nxt[tot] = head[from];
        head[from] = tot;
    
    int d[2 * maxn],val[2 * maxn],ans = -0x7fffffff;
    void dfs(int u)
        if(d[u])return;
        d[u] = val[u];
        for(int i = head[u];i;i = nxt[i])
            int v = to[i];
            dfs(v),d[u] = max(d[u],d[v] + val[u]);
        
        ans = max(ans,d[u]);
    
 
struct Point
    int x,y,opt;
point[maxn];
inline ll mhash(int x,int y)
    return x * c + y;

unordered_map<ll,int> mp;//hash存图,懒得写直接STL
int main()
    n = read(),r = read(),c = read();
    for(int x,y,opt,i = 1;i <= n;i++)
        point[i].x = x = read(),point[i].y = y = read(),point[i].opt = opt = read();
        mp[mhash(x,y)] = i;
        if(opt == 1)addedge(i,n + x);
        if(opt == 2)addedge(i,n + r + y);//向虚拟点连边
        line[x].push_back(i);
        column[y].push_back(i);//保存每行(列)有哪些点
    
    for(int i = 1;i <= r;i++)
        for(int v : line[i])
            addedge(n + i,v);
    for(int i = 1;i <= c;i++)
        for(int v : column[i])
            addedge(n + r + i,v);//虚拟点连边
    for(int i = 1;i <= n;i++)//w自w由w门w暴力连边
        if(point[i].opt == 3)
            for(int x = point[i].x - 1;x <= point[i].x + 1;x++)
                for(int y = point[i].y - 1;y <= point[i].y + 1;y++)
                    if(x >= 1 && x <= r && y >= 1 && y <= c && (x != point[i].x || y != point[i].y) && mp.find(mhash(x,y)) != mp.end())
                        addedge(i,mp[mhash(x,y)]);
    for(int i = 1;i <= n + r + c;i++)//缩点
        if(!dfn[i])tarjan(i);
    for(int u = 1;u <= n + r + c;u++)//在DAG新图上做dp
        for(int i = head[u];i;i = nxt[i])
                int v = to[i];
                if(col[u] != col[v])
                    Solve::addedge(col[u],col[v]);
            
    for(int i = 1;i <= col_tot;i++)//新图每个点的点权等于联通分量大小
        Solve::val[i] = siz[i];
    for(int i = 1;i <= col_tot;i++)//dp
        Solve::dfs(i);
    return printf("%d\n",Solve::ans),0;//Bye

那什么门是敏感词汇,醉了

以上是关于题解 P2403 [SDOI2010]所驼门王的宝藏的主要内容,如果未能解决你的问题,请参考以下文章

P2403 [SDOI2010]所驼门王的宝藏

P2403 [SDOI2010]所驼门王的宝藏

BZOJ 1924: [Sdoi2010]所驼门王的宝藏 题解

题解 [SDOI2010]所驼门王的宝藏

SDOI2010所驼门王的宝藏

[SDOI2010] 所驼门王的宝藏