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:
",tt);
79         for(int i=1;i<=q;i++) 
80         {
81             int x;
82             scanf("%d",&x);
83             if(x<=sum) printf("%d
",ans[x]);
84             else printf("-1
");
85         }
86     }
87     return 0;
88 }
原文地址:https://www.cnblogs.com/zarth/p/6561084.html