zoukankan      html  css  js  c++  java
  • SDOI2010 所驼门王的宝藏

    传送门

    这题的思路还是很清晰的,就是把图建出来之后用tarjan缩点,之后在DAG中用DP(或者说dfs)求出最长路就可以啦。

    不过事情是不会像想象中那么简单的。

    这个题目的图在建立的时候非常麻烦……首先我们不可能挨个枚举,这样复杂的最坏到O(n^2),建图都已经T了就没什么可做的了。

    所以我们考虑如何优化建图。

    根据题目描述,我们可以发现每一行的所有横天门成一个环,每一列的所有纵寰门成一个环,这样的话我们可以先进行排序,第一次按照建立横天门的顺序,按横坐标排序,横坐标相同的把横天门放在前面,还相同就按照纵坐标排序。因为我们见图的时候总是想先枚举到所有横天门,所以当然要这么建啦。然后这种建图方法同是还会连接这一行的横天门和自♂由门,纵寰门,但是不会连原来那么多条边了。

    纵寰门的建立方法和横天门是一样的。至于自♂由门,这个没什么太好的方法,不过由于一个自♂由门只和周围8个格子有关。所以直接暴力即可。不过要有点技巧,这个图太大了我们不能用数组存,所以开一个map,把用于表示当前点坐标的pair映射到一个bool值上,然后我们在建自♂由门的时候只要找周围8个点是否在map里(count函数)即可。

    之后就是经典的tarjan缩点操作,之后这里重新建图又用了一次map……直接重建也是可以的。

    最后在DAG中DP求出最长路即可。

    ovo代码远没有描述的这么简单……

    (别怪我写这么多哲学符号,谁叫自♂由门是敏感词汇……再说这题面本身就很自♂由)

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    #include<utility>
    #include<map>
    #define pr pair<int,int>
    #define mp make_pair
    #define fi first
    #define sc second
    #define rep(i,a,n) for(int i = a;i <= n;i++)
    #define per(i,n,a) for(int i = n;i >= a;i--)
    #define enter putchar('
    ')
    using namespace std;
    typedef long long ll;
    const int M = 100005;
    const int N = 10000005;
     
    int read()
    {
        int ans = 0,op = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
        {
        if(ch == '-') op = -1;
        ch = getchar();
        }
        while(ch >='0' && ch <= '9')
        {
        ans *= 10;
        ans += ch - '0';
        ch = getchar();
        }
        return ans * op;
    }
    
    int all,n,m,ind[M],r,c;
    map<pr,bool> mat;
    map<pr,int> tre;
    map<pr,bool> :: iterator it;
    int dx[9] = {0,-1,-1,-1,0,0,1,1,1},dy[9] = {0,-1,0,1,-1,1,-1,0,1},ecnt,head[M];
    int dfn[M],low[M],stack[M],top,belong[M],cnt,idx,sum[M],ans,dp[M];
    bool in[M];
    
    struct doors
    {
        int x,y,op,number;
    }dor[M];
    
    struct edge
    {
        int from,next,to;
    }e[M<<6];
    
    void add(int x,int y)
    {
        e[++ecnt].to = y;
        e[ecnt].from = x;
        e[ecnt].next = head[x];
        head[x] = ecnt;
    }
    
    bool htcmp(doors a,doors b)//横天门排序
    {
        if(a.x != b.x) return a.x < b.x;//先按照横坐标
        if(a.op == 1) return 1;//把横天门排在前
        if(b.op == 1) return 0;
        return a.y < b.y;//最后按纵坐标
    }
    
    bool zhcmp(doors a,doors b)//与上文同理
    {
        if(a.y != b.y) return a.y < b.y;
        if(a.op == 2) return 1;
        if(b.op == 2) return 0;
        return a.x < b.x;
    }
    
    void tarjan(int x)
    {
        dfn[x] = low[x] = ++idx;
        stack[++top] = x,in[x] = 1;
        for(int i = head[x];i;i = e[i].next)
        {
        if(!dfn[e[i].to]) tarjan(e[i].to),low[x] = min(low[x],low[e[i].to]);
        else if(in[e[i].to]) low[x] = min(low[x],dfn[e[i].to]);
        }
        if(low[x] == dfn[x])
        {
        cnt++;
        int p;
        while((p = stack[top--]))
        {
            belong[p] = cnt,in[p] = 0,sum[cnt]++;
            if(p == x) break;
        }
        }
    }
    
    void dfs(int x,int fa)
    {
        if(dp[x] > sum[x]) return;//剪枝
        dp[x] = sum[x];
        for(int i = head[x];i;i = e[i].next)
        {
        if(e[i].to == fa) continue;
        dfs(e[i].to,x);
        dp[x] = max(dp[x],dp[e[i].to] + sum[x]);
        }
    }
    
    void build1()
    {
        int fir = 1,la = 1;//fir是本行第一个横天门,la是当前最后一个
        rep(i,1,n)
        {
        if(dor[i].x != dor[i+1].x)//如果横坐标不同,也就是换行了
        {
            if(fir != la) add(dor[la].number,dor[fir].number);//连接上一行的第一个和最后一个横天门
            la = fir = i + 1;
        }
        else
        {
            if(dor[la].op == 1) add(dor[la].number,dor[i+1].number);//如果上一个是横天门就连上
            if(dor[i+1].op == 1) la = i+1;//更新最后一个横天门位置
            if(dor[fir].op != 1) la = fir = i+1;//更新第一个横天门位置
        }
        }
    }
    
    void build2()//与上面同理
    {
        int fir = 1,la = 1;
        rep(i,1,n)
        {
        if(dor[i].y != dor[i+1].y)
        {
            if(fir != la) add(dor[la].number,dor[fir].number);
            la = fir = i+1;
        }
        else
        {
            if(dor[la].op == 2) add(dor[la].number,dor[i+1].number);
            if(dor[i+1].op == 2) la = i+1;
            if(dor[fir].op != 2) la = fir = i+1;
        }
        }
    }
    
    void build3()
    {
        rep(i,1,n)
        {
        if(dor[i].op != 3) continue;
        rep(j,1,8)
        {
            int kx = dor[i].x + dx[j],ky = dor[i].y + dy[j];
            if(tre.count(mp(kx,ky))) add(dor[i].number,tre[mp(kx,ky)]);//如果这个点有宫室就建图
        }
        }
    }
    
    void rebuild()
    {
        rep(i,1,ecnt)
        {
        int r1 = belong[e[i].from],r2 = belong[e[i].to];
        if(r1 != r2) mat[mp(r1,r2)] = 1;
        }
        memset(head,0,sizeof(head)),ecnt = 0;
        for(it = mat.begin();it != mat.end();it++)//重新构造缩点之后的图
        add(it->fi.fi,it->fi.sc),ind[it->fi.sc]++;
    }
    
    int main()
    {
        n = read(),r = read(),c = read();
        rep(i,1,n)
        {
        dor[i].x = read(),dor[i].y = read();
        dor[i].op = read(),dor[i].number = i;
        tre[mp(dor[i].x,dor[i].y)] = i;//把这个点存到map里
        }
        sort(dor+1,dor+1+n,htcmp),build1();//建立横天门
        sort(dor+1,dor+1+n,zhcmp),build2();//建立纵寰门
        build3();//建立自♂由门
        rep(i,1,n) if(!dfn[i]) tarjan(i);//tarjan
        rebuild();//重新构图
        rep(i,1,cnt)
        {
        if(!ind[i]) dfs(i,0);//dp求最长路
        ans = max(ans,dp[i]);
        }
        printf("%d
    ",ans);
        return 0;
    }
  • 相关阅读:
    JAVA中获取路径
    maven 更换阿里镜像、设置本地仓库路径
    Cannot construct instance of `com.jty.entities.Dept` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate or propertybased Creator)
    oracle日期正则表达式
    linux配置jdk
    4月份健身计划
    刚才上了ednchina的blog,发现改版了。竟然登陆不上了
    ②这次将stm32的PC13作为普通i/o口驱动led,不知道能否发生网上提到的现象
    最近画的两块板子。
    RDS的板子推倒重画
  • 原文地址:https://www.cnblogs.com/captain1/p/9681269.html
Copyright © 2011-2022 走看看