HDU 5118 GRE Words Once More!
题目链接:HDU-5118
题意:给定一个有向无环图,每条边有一个权值。标定一些特定节点为“特殊节点”。从节点1出发到某“特殊节点”结束的路径,称为一个“GRE单词”。单词由路径上的权值组成。给定一组查询\(k_i\),问由给定的图产生的所有单词,按字典序排序后第\(k_i\)个单词的长度(即由多少条边组成)。
思路:首先这道题最吓人之处在于\(k_i<=10^{8}\),单纯的扫一遍所有可能出现的单词会超时。这时我们发现,输出时只要求输出单词长度,而不要求输出单词内容。由于是DAG,每个节点不会连回到祖宗节点。我们定义vis[i]为从节点i出发可以找到以“特殊节点”结尾的路径的数量。则可以得到递推关系:\(vis[u]=\sum_{V}{vis[v]}\)。同样的道理,我们记录第一次访问每个节点时,产生的所有以“特殊节点”结尾的路径。那么每个节点的子节点其实只需要遍历一次。之后访问时,直接加上之前的结果就可以了。
如果题解说的不是很明白,建议直接看代码,很容易看懂。
网上的一些题解说需要手动dfs,不过本人并没有手动也没有爆栈。建议谨慎地尝试。
1 #include"bits/stdc++.h" 2 using namespace std; 3 typedef long long LL; 4 5 // ans[i]为字典序第i的单词 6 // vis[i]为从节点i出发能找到以“特殊节点”结尾的路径的数量 7 // firstVis[i]为从节点i出发找到的"所有以“特殊节点”结尾的路径的深度"在ans中存储的起始位置 8 // firstVisDep[i]为第一次访问i时的深度 9 const int MAXN=100010; 10 const int MAXM=100000010; 11 struct Edge 12 { 13 int u,v,w; 14 Edge(int uu,int vv,int ww) 15 { 16 u=uu,v=vv,w=ww; 17 } 18 bool operator < (Edge x) 19 { 20 return w < x.w; 21 } 22 }; 23 vector<Edge> G[MAXN]; 24 int s[MAXN]; 25 int n,m,q; 26 int sum; 27 int ans[MAXM],vis[MAXN],firstVis[MAXN],firstVisDep[MAXN]; 28 void addEdge(int u,int v,int w) 29 { 30 G[u].push_back(Edge(u,v,w)); 31 } 32 void dfs(int u,int dep) 33 { 34 if(sum>=100000000) return; 35 if(vis[u]!=-1) 36 { 37 for(int i=1;i<=vis[u];i++) 38 { 39 if(sum>=100000000) return; 40 ans[++sum]=ans[firstVis[u]+i]+dep-firstVisDep[u]; 41 } 42 return ; 43 } 44 firstVis[u]=sum; 45 firstVisDep[u]=dep; 46 if(s[u]) ans[++sum]=dep; 47 for(unsigned int i=0;i<G[u].size();i++) 48 { 49 int v=G[u][i].v; 50 dfs(v,dep+1); 51 } 52 vis[u]=sum-firstVis[u]; 53 } 54 int main() 55 { 56 #ifdef LOCAL 57 freopen("in.txt","r",stdin); 58 // freopen("out.txt","w",stdout); 59 #endif 60 int t; 61 scanf("%d",&t); 62 for(int tt=1;tt<=t;tt++) 63 { 64 scanf("%d%d%d",&n,&m,&q); 65 for(int i=1;i<=n;i++) G[i].clear(); 66 s[1]=0; 67 for(int i=2;i<=n;i++) scanf("%d",&s[i]); 68 for(int i=1;i<=m;i++) 69 { 70 int u,v,w; 71 scanf("%d%d%d",&u,&v,&w); 72 addEdge(u,v,w); 73 } 74 for(int i=1;i<=n;i++) sort(G[i].begin(),G[i].end()); 75 memset(vis,-1,sizeof(vis)); 76 sum=0; 77 dfs(1,0); 78 printf("Case #%d:\n",tt); 79 for(int i=1;i<=q;i++) 80 { 81 int x; 82 scanf("%d",&x); 83 if(x<=sum) printf("%d\n",ans[x]); 84 else printf("-1\n"); 85 } 86 } 87 return 0; 88 }