A Robert 的火车旅行
题目大意 : 每个点只有一条出边,一共可以走k条边,对于每个点回答有多少个点可以到达自己这个点
- 不难发现图是一个内向基环树森林,环可以断环成链进行差分,除去环的部分就是树,树上差分就行
Code
Show Code
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 5e5 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar()) x = x * 10 + c - '0';
return x * f;
}
struct Edge {
int n, t;
}e[N];
int h[N], edc;
void Add(int x, int y) {
e[++edc] = (Edge) {h[x], y}; h[x] = edc;
}
bool v[N];
int n, k, to[N], stk[N], tp, a[N], cnt, b[N], w[N], s[N*2];
void Dfs(int x, int fa = 0) {
v[x] = 1;
stk[++tp] = x; w[x]++;
if (tp >= k) w[stk[tp-k]]--;
else s[b[stk[1]]+1]++, s[b[stk[1]]+min(k-tp+1, cnt)]--;
for (int i = h[x], y; i; i = e[i].n)
if (!b[y=e[i].t] && y != fa) Dfs(y, x), w[x] += w[y];
tp--;
}
int main() {
freopen("robert.in", "r", stdin);
freopen("robert.out", "w", stdout);
n = read(); k = read() + 1;
for (int i = 1; i <= n; ++i)
to[i] = read(), Add(to[i], i);
for (int j = 1; j <= n; ++j) {
if (v[j]) continue;
for (int x = j; !v[a[1] = x]; x = to[x]) v[x] = 1;
b[a[1]] = ++cnt;
for (int x = to[a[1]]; x != a[1]; x = to[x])
b[x] = ++cnt, a[cnt] = x;
for (int i = 1; i <= cnt; ++i) Dfs(a[i]);
for (int i = 1; i <= cnt * 2; ++i) s[i] += s[i-1];
for (int i = 1; i <= cnt; ++i) {
w[a[i]] += s[i] + s[i+cnt];
s[i] = s[i+cnt] = 0;
b[a[cnt]] = 0;
}
cnt = 0;
}
for (int i = 1; i <= n; ++i)
printf("%d
", w[i]);
return 0;
}
B 钻石教练老姚的神仙LIS
题目大意 : 求最长不降子序列,求最多可以同时找出多少个最长不降子序列,求a1,an可以重复选是最多可以同时找出多少个最长不降子序列
- 数据范围1e3,n2建边跑最大流即可,lis=1是记得特判
Code
Show Code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1005;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar()) x = x * 10 + c - '0';
return x * f;
}
struct Edge {
int n, t, f;
}e[N*N];
int h[N*2], edc, tmp[N*2];
void Add(int x, int y, int z) {
e[++edc] = (Edge) {h[x], y, z}; h[x] = edc;
e[++edc] = (Edge) {h[y], x, 0}; h[y] = edc;
}
int n, a[N], f[N], lis, q[N*2], dep[N*2], s, t;
bool Bfs() {
memset(dep + 1, 0, t * 4);
memcpy(h + 1, tmp + 1, t * 4);
int l = 1, r = 1; q[1] = s; dep[s] = 1;
while (l <= r) {
int x = q[l++];
for (int i = h[x], y; i; i = e[i].n) {
if (!e[i].f || dep[y=e[i].t]) continue;
dep[y] = dep[x] + 1; q[++r] = y;
if (y == t) return 1;
}
}
return 0;
}
int Dinic(int x, int lim) {
if (x == t) return lim;
int sum = 0;
for (int i = h[x]; i && lim; i = e[i].n) {
int y = e[i].t; h[x] = i;
if (!e[i].f || dep[y] != dep[x] + 1) continue;
int f = Dinic(y, min(lim, e[i].f));
lim -= f; sum += f;
e[i].f -= f; e[i^1].f += f;
}
if (!sum) dep[x] = 0;
return sum;
}
void Solve(bool g) {
memset(h + 1, 0, t * 4); edc = 1;
int ans = 0; s = n * 2 + 1; t = s + 1;
for (int i = 1; i <= n; ++i) {
int x = 1;
if (g && (i == 1 || i == n)) x = 1e3;
Add(i, i + n, x);
if (f[i] == 1) Add(s, i, x);
if (f[i] == lis) Add(i + n, t, x);
for (int j = 1; j < i; ++j)
if (a[j] <= a[i] && f[j] + 1 == f[i])
Add(j + n, i, 1);
}
memcpy(tmp + 1, h + 1, t * 4);
while (Bfs()) ans += Dinic(s, 1e3);
printf("%d
", ans);
}
int main() {
freopen("lis.in", "r", stdin);
freopen("lis.out", "w", stdout);
n = read();
for (int i = 1; i <= n; ++i)
a[i] = read();
for (int i = 1; i <= n; ++i) {
for (int j = 1; j < i; ++j)
if (a[j] <= a[i])
f[i] = max(f[i], f[j]);
lis = max(lis, ++f[i]);
}
printf("%d
", lis);
if (lis == 1) printf("%d
%d
", n, n);
else Solve(0), Solve(1);
return 0;
}
C 组合空间
题目大意 : 给m个子集,问有多少种方案可以组成全集
- 高维前缀和,子集反演,longdie说是最简单的签到题,改的时候感觉是3个里面最难的。可能是数学太差了吧
Code
Show Code
#include <cstdio>
using namespace std;
const int N = 1 << 22 | 5, M = 1e9 + 7;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar()) x = x * 10 + c - '0';
return x * f;
}
int n, m, ans, f[N], p[N];
int main() {
freopen("longdie.in", "r", stdin);
freopen("longdie.out", "w", stdout);
m = read(); n = read();
for (int i = 1; i <= n; ++i) {
int x = 0;
for (int k = read(); k; --k)
x |= 1 << read() - 1;
f[x ^ (1 << m) - 1]++;
}
p[0] = 1;
for (int i = 1; i <= n; ++i)
p[i] = 2 * p[i-1] % M;
for (int i = 1; i <= m; ++i)
for (int j = 0; j < 1 << m; ++j)
if (j & 1 << i - 1) (f[j^(1<<i-1)] += f[j]) %= M;
for (int s = (1 << m) - 1; s >= 0; --s) {
int cnt = 0, x = p[f[s]] - 1;
for (int i = s; i; i -= i & -i) cnt++;
if (cnt & 1) ans = (ans - x + M) % M;
else ans = (ans + x) % M;
}
printf("%d
", ans);
return 0;
}