题目链接:
题意:给定一个有向无环图,每条边有一个权值。标定一些特定节点为“特殊节点”。从节点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 Edge12 {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 vectorG[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