这题的思路还是很清晰的,就是把图建出来之后用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; }