记忆宫殿
初始入度为\(0\)的点为源点。
我们令 \(S\) 为如果成立,就能够推出事件的源点集合。
当事件成立时,显然 \(S\) 中的点必有至少一个是真的。所以我们只要把所有包含 \(S\) 的事件都标记为真就行了。
Pro-Professor Szu
某大学校内有一栋主楼,还有 栋住宅楼。这些楼之间由一些单向道路连接,但是任意两栋楼之间可能有多条道路,也可能存在起点和终点为同一栋楼的环路。存在住宅楼无法到达主楼的情况。
现在有一位古怪的教授,他希望每天去主楼上班的路线不同。
一条上班路线中,每栋楼都可以访问任意多次。我们称两条上班路线是不同的,当且仅当两条路线中存在一条路是不同的(两栋楼之间的多条道路被视为是不同的道路)。
现在教授希望知道,从哪些住宅楼前往主楼的上班路线数最多。
到达n+1后,可以选择停下或者继续走
首先发现若一个大小大于 1 的 SCC 或自环(下称为不合法结构)能够到达教学楼,则该不合法结构内部每个点到教学楼的路径数量都是无穷大。
显然可以先缩点,然后拓扑排序。
先将反图上入度为 0 的非教学楼点入队跑一遍拓扑排序。注意此时不合法结构可以入队,因为它们没有到达教学楼的路径。
最后,若出现没有入队的点,说明这个点能够到达一个不合法结构,因此路径数同样为无穷大。此外,若 \(f_i>36500\) 也不符合题意。
自己的代码没调出来,一直不过hack,贴一个题解的,过了就补(已)
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int n, m, ed, ban[N], deg[N], f[N];
int dn, dfn[N], low[N], cn, col[N], top, stc[N], vis[N];
struct linklist {int cnt, hd[N], nxt[N], to[N];void add(int u, int v) {nxt[++cnt] = hd[u], hd[u] = cnt, to[cnt] = v;}
} e, g;
void tarjan(int id) {dfn[id] = low[id] = ++dn, stc[++top] = id, vis[id] = 1; // 0 -> 1for(int _ = e.hd[id]; _; _ = e.nxt[_]) {int it = e.to[_];if(!dfn[it]) tarjan(it), low[id] = min(low[id], low[it]);else if(vis[it]) low[id] = min(low[id], dfn[it]);}if(low[id] == dfn[id]) {col[id] = ++cn, ban[cn] = stc[top] != id;while(stc[top] != id) col[stc[top]] = cn, vis[stc[top--]] = 0; // id -> cnvis[id] = 0, top--;}
}
int main() {
#ifdef ALEX_WEIfreopen("1.in", "r", stdin);freopen("1.out", "w", stdout);
#endifcin >> n >> m;for(int i = 1; i <= m; i++) {int u, v;scanf("%d%d", &u, &v);e.add(u, v);}for(int i = 1; i <= n + 1; i++) if(!dfn[i]) tarjan(i);for(int i = 1; i <= n + 1; i++)for(int _ = e.hd[i]; _; _ = e.nxt[_]) {int it = e.to[_];if(i == it) ban[col[i]] = 1;else if(col[i] != col[it]) g.add(col[it], col[i]), deg[col[i]]++;}ed = col[n + 1];queue<int> q;for(int i = 1; i <= cn; i++) if(i != ed && !deg[i]) q.push(i);memset(vis, 0, sizeof(vis));while(!q.empty()) {int t = q.front();q.pop(), vis[t] = 1;for(int _ = g.hd[t]; _; _ = g.nxt[_]) {int it = g.to[_];if(!--deg[it] && it != ed) q.push(it);}}if(!ban[ed]) assert(!deg[ed]), q.push(ed), f[ed] = 1;while(!q.empty()) {int t = q.front();q.pop(), vis[t] = 1;for(int _ = g.hd[t]; _; _ = g.nxt[_]) {int it = g.to[_];if(ban[it]) continue;f[it] = min(36501, f[it] + f[t]);if(!--deg[it]) q.push(it);}}vector<int> ans;for(int i = 1; i <= n; i++)if(!vis[col[i]] || f[col[i]] == 36501)ans.push_back(i);if(!ans.empty()) puts("zawsze");else {int mx = 0;for(int i = 1; i <= n; i++) {if(f[col[i]] > mx) mx = f[col[i]], ans.clear();if(f[col[i]] == mx) ans.push_back(i);}cout << mx << "\n";}cout << ans.size() << "\n";for(int it : ans) cout << it << " ";return cerr << "Time: " << clock() << endl, 0;
}
POI2010 Antisymmetry
对于一个01字符串,如果将这个字符串0和1取反后,再将整个串反过来和原串一样,就称作“反对称”字符串。比如00001111和010101就是反对称的,1001就不是。
现在给出一个长度为N的01字符串,求它有多少个子串是反对称的。
manacher板子,写就完了
#include <bits/stdc++.h>
#define ull long long
const int maxn = 1e7;
char SS1[maxn], S[maxn], to[500];
int n, len[maxn], tot = 1;
signed main() {scanf("%d%s", &n, SS1 + 1);S[0] = '$', S[1] = '#';for (register int i = 1; i <= n; ++i) S[++tot] = SS1[i], S[++tot] = '#';to['1'] = '0', to['0'] = '1', to['#'] = '#', to['$'] = '$';int pos = 1, mx = 1;ull ans = 0;for (register int i = 1; i <= tot; i += 2) {len[i] = (i < mx ? std::min(mx - i, len[(pos << 1) - i]) : 1);while (S[i + len[i]] == to[S[i - len[i]]]) len[i]++;if (len[i] + i > mx) {mx = len[i] + i;pos = i;}ans += len[i] >> 1;}printf("%llu\n", ans);return 0;
}