2019.12.30考试总结

T1摆棋子
看到这么多的限制,第一直觉就是网络流,最小费用最大流不能做,因为把棋子代价看成1后最大流的条件并不好满足。
注意到每一行每一列都有一个最小限制,对应到网络流上就是流的下界,整体跑一个有下界的最小流就可以了。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define LL long long
#define DB double
using namespace std;
int n, m, k, x, y, tot = 1, S, T, SS, TT, ans, flow;
const int N = 105, inf = 1 << 30;
int X[N], Y[N], vis[N][N], d[N * N], A[N * N];
int head[N * N];
struct bian 
{
	int to, nt, cap;
} e[N * N * 4];
void add(int f, int t, int c) 
{
	e[++tot] = (bian) {t, head[f], c};
	head[f] = tot;
}
void ADD(int f, int t, int c) 
{
	add(f, t, c); add(t, f, 0);
}
inline int read() 
{
	int res = 0; char ch = getchar(); bool XX = false;
	for (; !isdigit(ch); ch = getchar())(ch == '-') && (XX = true);
	for (; isdigit(ch); ch = getchar())res = (res << 3) + (res << 1) + (ch ^ 48);
	return XX ? -res : res;
}
queue<int>q;
int bfs(int S, int T) 
{
	int x;
	memset(d, 0, sizeof(d));
	while (!q.empty())q.pop();
	q.push(S); d[S] = 1;
	while (!q.empty()) 
	{
		x = q.front(); q.pop();
		for (int i = head[x]; i; i = e[i].nt)
			if (e[i].cap && !d[e[i].to]) 
			{
				d[e[i].to] = d[x] + 1;
				q.push(e[i].to);
				if (e[i].to == T)return 1;
			}
	}
	return 0;
}
int dinic(int x, int flow, int T) 
{
	if (x == T || !flow)return flow;
	int res = flow, k;
	for (int i = head[x]; i; i = e[i].nt)
		if (e[i].cap && d[e[i].to] == d[x] + 1) 
		{
			k = dinic(e[i].to, min(e[i].cap, res), T);
			if (!k)d[e[i].to] = 0;
			e[i].cap -= k; e[i ^ 1].cap += k;
			res -= k;
		}
	return flow - res;
}
bool pan() 
{
	int js;
	for (int i = 1; i <= n; ++i) 
	{
		js = 0;
		for (int j = 1; j <= m; ++j)
			if (!vis[i][j])++js;
		if (js < X[i])return 1;
	}
	for (int j = 1; j <= m; ++j) 
	{
		js = 0;
		for (int i = 1; i <= n; ++i)
			if (!vis[i][j])++js;
		if (js < Y[j])return 1;
	}
	return 0;
}
int main() 
{
	freopen("chessman.in", "r", stdin);
	freopen("chessman.out", "w", stdout);
	cin >> n >> m >> k; S = n + m + 1, T = n + m + 2, SS = n + m + 3, TT = n + m + 4;
	for (int i = 1; i <= n; ++i)X[i] = read();
	for (int i = 1; i <= m; ++i)Y[i] = read();
	for (int i = 1; i <= k; ++i) 
	{
		x = read(); y = read();
		vis[x][y] = 1;
	}
	if (pan()) 
	{
		puts("No Solution");
		fclose(stdin); fclose(stdout);
		return 0;
	}
	for (int i = 1; i <= n; ++i) 
	{
		ADD(S, i, inf - X[i]);
		A[S] -= X[i]; A[i] += X[i];
	}
	for (int i = 1; i <= m; ++i) 
	{
		ADD(n + i, T, inf - Y[i]);
		A[n + i] -= Y[i]; A[T] += Y[i];
	}
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j)
			if (!vis[i][j])ADD(i, n + j, 1);
	for (int i = 1; i <= n + m + 2; ++i)
		if (A[i] > 0) {ADD(SS, i, A[i]);}
		else if (A[i])ADD(i, TT, -A[i]);
	while (bfs(SS, TT))while (flow = dinic(SS, inf, TT))ans += flow;
	ADD(T, S, inf);
	while (bfs(SS, TT))while (flow = dinic(SS, inf, TT))ans += flow;
	cout << e[tot].cap;
	fclose(stdin); fclose(stdout);
	return 0;
}

T2旅行路线
把度数看成一个字符,就变成了树上的不同子串个数问题。
一个序列的我们能解决,放到树上只需要把last改成fa的id就行了。

#include<iostream>
#include<cstdio>
#include<set>
#include<map>
#define LL long long
#define DB double
using namespace std;
int n, tot, x, y, cnt;
const int N = 1000010;
int head[N], du[N], id[N], len[N], fa[N];
map<int, int>tr[N];
struct bian {int to, nt;} e[N];
LL ans;
void add(int f, int t) 
{
	e[++tot] = (bian) {t, head[f]};
	head[f] = tot;
}
inline int read() 
{
	int res = 0; char ch = getchar(); bool XX = false;
	for (; !isdigit(ch); ch = getchar())(ch == '-') && (XX = true);
	for (; isdigit(ch); ch = getchar())res = (res << 3) + (res << 1) + (ch ^ 48);
	return XX ? -res : res;
}
void Insert(int x, int Q, int las) 
{
	int np = ++cnt, p = las, q, nq;
	len[np] = len[p] + 1; id[Q] = cnt;
	for (; p && tr[p].find(x) == tr[p].end(); p = fa[p])tr[p][x] = np;
	if (!p)fa[np] = 1;
	else if (len[q = tr[p][x]] == len[p] + 1)fa[np] = q;
	else 
	{
		len[nq = ++cnt] = len[p] + 1;
		fa[nq] = fa[q]; fa[q] = fa[np] = nq; tr[nq] = tr[q];
		for (; p; p = fa[p]) 
		{
			if (tr[p].find(x) == tr[p].end() || tr[p][x] != q)break;
			tr[p][x] = nq;
		}
	}
}
void dfs(int x, int fa) 
{
	Insert(du[x], x, id[fa]);
	for (int i = head[x]; i; i = e[i].nt)
		if (e[i].to != fa)dfs(e[i].to, x);
}
int main() 
{
	freopen("route.in", "r", stdin);
	freopen("route.out", "w", stdout);
	cnt = id[0] = 1; cin >> n;
	for (int i = 1; i < n; ++i) 
	{
		x = read(); y = read();
		add(y, x); du[x]++; du[y]++;
	}
	dfs(1, 0);
	for (int i = 2; i <= cnt; ++i)ans += len[i] - len[fa[i]];
	cout << ans;
	fclose(stdin); fclose(stdout);
	return 0;
}

T3流浪者
总的方案数就是(C_{n+m-2}^{n-1}),因为体力减小的快,只有log种取值,所以总的体力是对每一种s乘上相应的方案数的和。
f[i][j] 表示从起点出发经过恰好 j 个特殊点到达第 i 个障碍点的方案,依然考虑容斥,枚举不合法路径上除 i 之外的第 j 个特殊点
(displaystyle f[i][j]=C_{x_i+y_i}^{x_i} - sum_{k=1}^{i-1}f[k][j]·ways(k, i) - sum_{k=1}^{j-1}f[i][k])
ways(i, j) 表示从第 i 个特殊点到第 j 个障碍点的方案
不懂的话可以吧右边的减号移到左边来。

#include<algorithm>
#include<iostream>
#include<cstdio>
#define int long long
#define LL long long
#define DB double
using namespace std;
int n, m, k, s, x, y, mx;
const int mod = 1e9 + 7, N = 200010, M = 2005;
int ex[M];
LL jc[N], inv[N], f[M][M], w[M][M], sum[M][M];
LL ans;
inline int read() 
{
	int res = 0; char ch = getchar(); bool XX = false;
	for (; !isdigit(ch); ch = getchar())(ch == '-') && (XX = true);
	for (; isdigit(ch); ch = getchar())res = (res << 3) + (res << 1) + (ch ^ 48);
	return XX ? -res : res;
}
struct dian 
{
	int x, y;
	friend bool operator <(const dian &a, const dian &b) {return a.x + a.y < b.x + b.y;}
} p[M];
LL ksm(LL a, LL b, LL mod) 
{
	LL res = 1;
	for (; b; b >>= 1, a = a * a % mod)
		if (b & 1)res = res * a % mod;
	return res;
}
void YYCH() 
{
	jc[0] = jc[1] = inv[0] = inv[1] = 1;
	for (int i = 2; i <= n + m; ++i)jc[i] = jc[i - 1] * i % mod;
	inv[n + m] = ksm(jc[n + m], mod - 2, mod);
	for (int i = n + m - 1; i >= 1; --i)inv[i] = inv[i + 1] * (i + 1) % mod;
}
LL C(LL n, LL m) 
{
	if (n < m || n < 0 || m < 0)return 0;
	return jc[n] * inv[m] % mod * inv[n - m] % mod;
}
inline LL ni(LL x) {return ksm(x, mod - 2, mod);}
signed main() 
{
	freopen("rover.in", "r", stdin);
	freopen("rover.out", "w", stdout);
	cin >> n >> m >> k >> s;
	YYCH();
	for (int i = 1; i <= k; ++i)
		p[i].x = read(), p[i].y = read();
	p[0] = (dian) {1, 1}; p[++k] = (dian) {n, m};
	sort(p, p + k + 1); ex[0] = s;
	for (int i = 1; i <= 20; ++i) 
	{
		ex[i] = (ex[i - 1] + 1) >> 1;
		if (ex[i] == 1 && !mx)mx = i;
	}
	f[0][0] = 1;
	for (int i = 0; i <= k; ++i)
		for (int j = 0; j < i; ++j)
			w[i][j] = C(p[i].x - p[j].x + p[i].y - p[j].y, p[i].x - p[j].x);
	for (int i = 1; i <= k; ++i)
		for (int j = 0; j <= mx + 1; ++j) 
		{
			f[i][j] = C(p[i].x + p[i].y - 2, p[i].x - 1);
			if (j)f[i][j] = (f[i][j] - sum[i][j - 1] + mod) % mod;
			for (int l = 0; l < i; ++l)
				f[i][j] = (f[i][j] - f[l][j] * w[i][l] % mod + mod) % mod;
			if (j)sum[i][j] = (sum[i][j - 1] + f[i][j]) % mod;
			else sum[i][j] = f[i][j];
		}
	for (int i = 1; i <= mx; ++i)
		(ans += f[k][i] * ni(C(n + m - 2, n - 1)) % mod * ex[i - 1] % mod) %= mod;
	ans = (ans + (C(n + m - 2, n - 1) - sum[k][mx] + mod) % mod * ni(C(n + m - 2, n - 1)) % mod) % mod;
	cout << ans;
	fclose(stdin); fclose(stdout);
	return 0;
}
原文地址:https://www.cnblogs.com/wljss/p/12121561.html