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('\n')
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\n",ans);
    return 0;
}

 

posted @ 2018-09-20 15:41  CaptainLi  阅读(155)  评论(0编辑  收藏  举报