zoukankan      html  css  js  c++  java
  • 题解 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
    ",Solve::ans),0;//Bye
    }
    

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

  • 相关阅读:
    记坑
    常用模板
    ACM-东北赛划水记
    jzoj 4178游戏
    JZOI 4163
    jzoj 4146踩气球
    jzoj 5589. 缩点
    jzoj 5588 %%%
    jzoj 5571 ffs
    BJOI 2017 Kakuro
  • 原文地址:https://www.cnblogs.com/colazcy/p/11515072.html
Copyright © 2011-2022 走看看