constint N = 1e5+10; int h[N], e[N], ne[N], idx, d[N]; int n, m; int q[N];
voidadd(int a, int b){ e[idx] = b, ne[idx] = h[a], h[a] = idx++; // 入度 d[b]++; }
booltopsort(){ int tt = -1, hh = 0; for (int i = 1; i <= n; i++) { // 这里是 d[i] 代表第 i 节点的入度 所以从 1 ~ n 而不是从 0 开始 if (d[i] == 0) q[++tt] = i; } while (hh <= tt) { int t = q[hh++]; for (int i = h[t]; i != -1; i = ne[i]) { int j = e[i]; d[j]--; // 添加到队列中的是树的节点 if (d[j] == 0) q[++tt] = j; } } // 如果所有节点都被遍历过,那么拓扑序列是存在的 return tt == n-1; }
intmain(){ memset(h, -1, sizeof h); cin >> n >> m; int a, b; for (int i = 0; i < m; i++) { cin >> a >> b; add(a, b); } if (topsort()) { for (int i = 0; i < n; i++) cout << q[i] << ' '; } elseputs("-1"); }
typedeflonglong LL; constint N = 200010, M = 1200010, P = 998244353; int topo[N]; int h[N], e[M], ne[M], cnt[N], add[N], mul[N], type[N], din[N], p[N], idx; int val[N/2]; int n, m, q;
voidaddedge(int a, int b){ e[idx] = b, ne[idx] = h[a], h[a] = idx++; din[b]++; }
voidtopsort(){ int hh = 0, tt = -1; for (int i = 0; i <= n+m; i++) { if (din[i] == 0) topo[++tt] = i; }
while (hh <= tt) { int t = topo[hh++]; for (int i = h[t]; ~i; i = ne[i]) { int j = e[i]; if (--din[j] == 0) topo[++tt] = j; } } }
voidpushup(){ for (int i = n+m; ~i; i--) { int u = topo[i]; for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; mul[u] = (LL)mul[u] * mul[j] % P; } } }
voidpushdown(){ for (int i = 0; i <= n+m; i++) { LL prod = 1; int u = topo[i]; for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; cnt[j] = (cnt[j] + prod * cnt[u]) % P; prod = prod * mul[j] % P; } } }
scanf("%d", &m); fill(mul, mul+n+m+1, 1); for (int i = 1; i <= m; i++) { int u = i+n; scanf("%d", &type[u]); if (type[u] == 1) scanf("%d%d", &p[u], &add[u]); if (type[u] == 2) scanf("%d", &mul[u]); if (type[u] == 3) { int c, f; scanf("%d", &c); while (c--) { scanf("%d", &f); addedge(u, f+n); } } }
scanf("%d", &q); for (int i = 1, f; i <= q; i++) { scanf("%d", &f); addedge(0, f+n); }
cnt[0] = 1; topsort(); pushup(); pushdown();
for (int i = 0; i <= n+m; i++) if (type[i] == 1) val[p[i]] = (val[p[i]] + (LL)add[i] * cnt[i]) % P;
for (int i = 1; i <= n; i++) printf("%d ", val[i]); puts("");
return0; }
[NOIP2013] 车站分级
一条单向的铁路线上,依次有编号为 1,2,…,n 个火车站。
每个火车站都有一个级别,最低为 1 级。
现有若干趟车次在这条线路上行驶,每一趟都满足如下要求:如果这趟车次停靠了火车站 x,则始发站、终点站之间所有级别大于等于火车站 x 的都必须停靠。(注意:起始站和终点站自然也算作事先已知需要停靠的站点) 。
constint N = 2010, M = 1000010; int n, m; int h[N], e[M], ne[M], w[M], din[N], dist[N], idx; bool st[N]; int q[N];
intread(){ int x = 0; char ch = getchar(); while (ch < '0' || ch > '9') ch = getchar(); while ('0' <= ch && ch <= '9') { x = x*10 + (ch & 15); ch = getchar(); } return x; }
voidadd(int a, int b, int c){ e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++; din[b]++; }
voidtopsort(){ int hh = 0, tt = -1; for (int i = 1; i <= m+n; i++) { if (din[i] == 0) q[++tt] = i, dist[i] = 1; } while (hh <= tt) { int t = q[hh++]; for (int i = h[t]; ~i; i = ne[i]) { int j = e[i]; if (--din[j] == 0) q[++tt] = j; } } }
intsolve(){ for (int i = 0; i < m+n; i++) { int t = q[i]; for (int j = h[t]; ~j; j = ne[j]) { int k = e[j]; dist[k] = max(dist[k], dist[t] + w[j]); } } return *max_element(dist+1, dist+1+n); }
intmain(){ n = read(), m = read(); memset(h, -1, sizeof h); for (int i = 1; i <= m; i++) { memset(st, 0, sizeof st); int cnt = read(); int start = n, end = 0; while (cnt--) { int x = read(); st[x] = true; start = min(start, x), end = max(end, x); } int v = n+i; for (int j = start; j <= end; j++) if (st[j]) add(v, j, 0); elseadd(j, v, 1); } topsort(); printf("%d\n", solve()); return0; }
[COCI2016-2017#1] Cezar
Mirko 想对 n 个单词进行加密。加密过程是这样的:
选择一个英文字母表的排列作为密钥。
将单词中的 a 替换为密钥中的第一个字母,b 替换为密钥中的第二个字母……以此类推。
例如,以 qwertyuiopasdfghjklzxcvbnm 作为密钥对 cezar 加密后,将得到 etmqk。
他希望,将所有单词加密并按字典序升序排列后,最初的第 ai 个单词位于第 i 位。请你判断,这能否实现。如果能,请给出任意一种可行的密钥。
constint N = 110; int n, din[N], q[N]; char s[N][N], *p[N]; vector<int> g[N];
booltopsort(){ int hh = 0, tt = -1; for (int i = 0; i < 26; i++) if (!din[i]) q[++tt] = i; while (hh <= tt) { int t = q[hh++]; for (int v: g[t]) if (--din[v] == 0) q[++tt] = v; } return tt == 26-1; }
intdiff(constchar* a, constchar* b){ int p = strlen(a), q = strlen(b); for (int i = 0; i < max(p, q); i++) { if (a[i] != b[i]) return i; } return-1; }
intmain(){ scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%s", s[i]); for (int i = 1, a; i <= n; i++) scanf("%d", &a), p[i] = s[a];
for (int i = 1; i <= n; i++) { int presame = i; for (int j = i+1; j <= n; j++) { int k = diff(p[i], p[j]); if (k == -1) { if (j != presame + 1) puts("NE"), exit(0); presame = j; continue; } if (!p[i][k] || !p[j][k]) { if (strlen(p[i]) > strlen(p[j])) puts("NE"), exit(0); continue; } g[p[i][k]-'a'].emplace_back(p[j][k]-'a'); din[p[j][k]-'a']++; } }
if (!topsort()) returnputs("NE"), 0;
staticchar res[30]; for (int i = 0; i < 26; i++) res[q[i]] = 'a' + i; return !printf("DA\n%s\n", res); }
[HNOI2015] 菜肴制作
知名美食家小 A 被邀请至 ATM 大酒店,为其品评菜肴。ATM 酒店为小 A 准备了 n 道菜肴,酒店按照为菜肴预估的质量从高到低给予 1 到 n 的顺序编号,预估质量最高的菜肴编号为 1。
由于菜肴之间口味搭配的问题,某些菜肴必须在另一些菜肴之前制作,具体的,一共有 m 条形如 i 号菜肴必须先于 j 号菜肴制作的限制,我们将这样的限制简写为 (i,j)。
constint N = 100010; int n, m, ans[N], din[N]; vector<int> g[N];
booltopsort(){ priority_queue<int> heap;
int cnt = 0; for (int i = 1; i <= n; i++) if (!din[i]) heap.push(i); while (heap.size()) { int t = heap.top(); heap.pop(); ans[cnt++] = t; for (int v: g[t]) { if (--din[v] == 0) heap.push(v); } } return cnt == n; }
voidsolve(){ scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) din[i] = 0, vector<int>().swap(g[i]); for (int i = 0, a, b; i < m; i++) { scanf("%d%d", &a, &b); g[b].emplace_back(a); din[a]++; } if (!topsort()) returnputs("Impossible!"), void(); for (int i = n-1; i >= 0; i--) printf("%d ", ans[i]); puts(""); }
intmain(){ int T; scanf("%d", &T); while (T--) solve(); return0; }