题目 P4376 [USACO18OPEN] Milking Order G
题解
首先我们发现 这个可以满足序列的数目是单调的
所以 我们可以直接二分答案来确实满足了几个序列
然后 我们发现 如果把先后顺序加成有向图的表示方式
我们就需要判断一下这个图中是否存在环
可以使用Tarjan算法判断
如果是一个DAG(有向无环图)的话就是合理的(因为存在拓扑序,这道题的题意契合拓扑序)
最后 我们根据满足的序列数再建一个DAG
使用拓扑排序确定答案序列
还有 保证字典序最小
我们只需要把拓扑排序中的队列换成优先队列就可以了(因为是数字)
代码
#include<bits/stdc++.h>
const int M = 1e5 + 10, N = 5e5 + 10;
using namespace std;
int n, m, cnt, ans, top;
int num[M], ansq[M];
int to[N], nxt[N], h[N], tot;
//tarjan用
int dfn[M], low[M], st[M], in[M];
bool vis[M];
//拓扑用
vector<int> G[M];//建拓扑序的边
priority_queue<int, vector<int>, greater<int> > q;
void add(int x, int y){ to[++ tot] = y; nxt[tot] = h[x]; h[x] = tot; }
void trjan(int now)
{
dfn[now] = low[now] = ++ cnt;
st[++ top] = now;
vis[now] = 1;
for(int i = h[now]; i; i = nxt[i])
{
int v = to[i];
if(!dfn[v]) trjan(v), low[now] = min(low[now], low[v]);
else if(vis[v]) low[now] = min(low[now], dfn[v]);
}
if(dfn[now] == low[now])
{
++ tot;
while(st[top + 1] != now)
vis[st[top --]] = 0;
}
}
//二分答案判断函数
bool check(int x)
{
//重建图:仅到第mid行
tot = 0;
memset(h, 0, sizeof h);
for(int i = 1; i <= x; ++i)
for(int j = 0; j < (int)G[i].size() - 1; ++j)
add(G[i][j], G[i][j+1]);
tot = 0, cnt = 0;
memset(vis, 0, sizeof vis);
memset(dfn, 0, sizeof dfn);
memset(low, 0, sizeof low);
top = 0;
for(int i = n; i; --i) if(!dfn[i]) trjan(i);
if(tot == n) return 1;
else return 0;
}
//拓扑排序求解答案
void tuopu(int now)
{
//重建图
tot = 0;
memset(h, 0, sizeof h);
for(int i = 1; i <= now; ++i)
for(int j = 0; j < (int)G[i].size() - 1; ++ j )
add(G[i][j], G[i][j + 1]), in[G[i][j + 1]] ++;
cnt = 0;
for(int i = 1; i <= n; ++ i) if(!in[i]) q.push(i);
while(!q.empty() )
{
int u = q.top();
q.pop();
ansq[++ cnt] = u;
for(int i = h[u]; i; i = nxt[i])
{
int v = to[i];
in[v]--;
if(!in[v]) q.push(v);
}
}
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; i ++)
{
scanf("%d", &num[i]);
for(int j = 1, x; j <= num[i]; j ++)
scanf("%d", &x), G[i].push_back(x);
}
//二分答案确定ans
int l = 1, r = n, ans = 0;
while(l <= r)
{
int mid = (l + r) >> 1;
if(check(mid)) ans = mid, l = mid + 1;
else r = mid - 1;
}
tuopu(ans);
for(int i = 1; i <= n; ++i) printf("%d ", ansq[i]);
return 0;
}