题目大意
这是第二题,仍然是一道送分题。
给出了一个$N$个点$M$条边的有向图。
求有多少个有序点对$(a,b)$,满足至少存在一个点$c$以及从$c$到$a$的一条路径$L_a$,$c$到$b$的一条路径$L_b$,使得$L_a$的长度是$L_b$的两倍(长度指的经过的边的数目)
注意不一定是简单路径。
题目分析
输出$n\times n$,爆零。。。
类似的一道题,我们通过确定搜索图的状态进行动态规划。
不难发现,设$f[x,y]$表示从某个$c$出发,$a$在$x$,$b$在$y$时是否合法。
显然,我们有一个$O(nm^2)$的算法。
即枚举$x$的后继与$y$的二倍后继进行转移。
我们可以另设状态使转移分步。
设$f[i,j,k=0/1/2]$分别表示当前状态为$(i,j)$,轮到$i$走($0$),轮到$j$走($1/2$)是否合法。
$k=0/1/2$只能转移到$(k+1)\bmod\,3$。
在$k=0$时统计答案
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| #include<algorithm> #include<iostream> #include<iomanip> #include<cstring> #include<cstdlib> #include<climits> #include<vector> #include<cstdio> #include<cmath> #include<queue> using namespace std;
inline const int Get_Int() { int num=0,bj=1; char x=getchar(); while(x<'0'||x>'9') { if(x=='-')bj=-1; x=getchar(); } while(x>='0'&&x<='9') { num=num*10+x-'0'; x=getchar(); } return num*bj; }
const int maxn=3005;
vector<int>edges[maxn];
void AddEdge(int x,int y) { edges[x].push_back(y); }
struct QueNode { int x,y,t; QueNode(int _x=0,int _y=0,int _t=0):x(_x),y(_y),t(_t) {} };
queue<QueNode>Q;
int n,m,ans; bool vst[maxn][maxn][3];
int main() { n=Get_Int(); m=Get_Int(); for(int i=1; i<=m; i++) { int x=Get_Int(),y=Get_Int(); AddEdge(x,y); } for(int i=1; i<=n; i++)vst[i][i][0]=1,Q.push(QueNode(i,i,0)); ans=n; while(!Q.empty()) { QueNode Now=Q.front(); int x=Now.x,y=Now.y,t=Now.t; Q.pop(); if(t==0) { for(int Next:edges[x]) { if(vst[Next][y][1])continue; vst[Next][y][1]=1; Q.push(QueNode(Next,y,1)); } } else { int nextt=(t+1)%3; for(int Next:edges[y]) { if(vst[x][Next][nextt])continue; vst[x][Next][nextt]=1; if(nextt==0)ans++; Q.push(QueNode(x,Next,nextt)); } } } printf("%d\n",ans); return 0; }
|