CF diary III

9.22-10.27
\(10\) 题一篇 \(\texttt{>o<}\)

P6008 [USACO20JAN]Cave Paintings P

题意

一幅 \(n\cdot m(1\le n,m\le1000)\) 的画,边界都是石头,内部有空地或者石头,现在在空地上画水,求有多少画水的方案使得这些水会稳定存在,每行的高度设为 \(n-i+1\)

题解

考虑从下到上扫描每行,用并查集来维护连通块,一开始各点的方案数都置为 \(1\) ,即不画,之后扫描行时每合并两个以扫描过的连通块,就将他们的方案数相乘作为新连通块方案数,因为之前被阻隔所以在两部分画水的操作是独立的。最后还要给合并后剩下的每个连通块方案数 \(+1\) 因为在最新一行可以选择放来填满整个连通块,最后个连通块结果相加即可。

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mk make_pair
//#define int LL
#define lc p*2
#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const LL MOD = 1e9 + 7;
const LL mod = 998244353;
const int maxn = 1010;

char A[maxn][maxn];
LL N, M, sum[maxn * maxn],  fa[maxn * maxn], rk[maxn * maxn];

void init()
{
	for (int i = 1; i <= N * M; i++)
		fa[i] = i, rk[i] = 0, sum[i] = 1;
}

int find(int x)
{
	if (fa[x] == x)
		return x;
	return fa[x] = find(fa[x]);
}

int idx(int i, int j)
{
	return (i - 1) * M + j;
}

void unite(int x, int y)
{
	x = find(x), y = find(y);
	if (x == y)
		return;
	if (rk[x] < rk[y])
		fa[x] = y, sum[y] = sum[y] * sum[x] % MOD;
	else
	{
		fa[y] = x, sum[x] = sum[x] * sum[y] % MOD;
		if (rk[x] == rk[y])
			rk[x]++;
	}
}

bool same(int x, int y)
{
	return find(x) == find(y);
}

void solve()
{
	init(); LL ans = 1;
	for (int i = N - 1; i > 1; i--)
	{
		for (int j = 2; j < M; j++)
		{
			if (A[i][j] == '.')
			{
				if (A[i][j - 1] == '.')
					unite(idx(i, j), idx(i, j - 1));
				if (A[i + 1][j] == '.')
				{
					if (!same(idx(i, j), idx(i + 1, j)))
						unite(idx(i, j), idx(i + 1, j));
				}
			}
		}
		set<int>S;
		for (int j = 2; j < M; j++)
		{
			if (A[i][j] == '.')
				S.insert(find(idx(i, j)));
		}
		for (auto& f : S)
			sum[f] = (sum[f] + 1) % MOD;
	}
	for (int i = 2; i < N; i++)
	{
		for (int j = 2; j < M; j++)
		{
			if (A[i][j] == '.' && fa[idx(i, j)] == idx(i, j))
				ans = ans * sum[idx(i, j)] % MOD;
		}
	}
	cout << ans << endl;
}

int main()
{
	IOS;
	cin >> N >> M;
	for (int i = 1; i <= N; i++)
	{
		for (int j = 1; j <= M; j++)
			cin >> A[i][j];
	}
	solve();

	return 0;
}

P5488 差分与前缀和

题意

求给定序列的 \(k\) 阶前缀和与差分。

题解

对于一个序列的 \(\texttt{OGF}\) ,其乘上 \(\sum_{i=0}^{\infty}x_i\) 相当于做一次前缀和,乘上 \(1-x\) 相当于做一次差分,做 \(k\) 阶分别乘上 \(\frac{1}{(1-x)^k}\)\((1-x)^k\) 。二者分别用(广义)二项式定理展开后再与原序列 \(\texttt{OGF}\) 卷积即可,复杂度 \(O(nlogn)\)

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
#define int LL
//#define double LD
//#define lc p*2
//#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 1004535809;
const int maxn = 1e7 + 5;

inline int po(int x, int y)
{
	if (y < 0) return 0;
	int r = 1;
	for (; y; y >>= 1, x = mul(x, x)) if (y & 1) r = mul(r, x);
	return r;
}

int n, k, t, inv[maxn], w[maxn];
string kk;

namespace poly
{
	int r[maxn], a[maxn], b[maxn], w[maxn], iw[maxn], m, l;
	void init(int x)
	{
		for (m = 1, l = 0; m <= x; m <<= 1, ++l)
			continue;
		for (int i = 0; i < m; ++i) 
			r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
		int b = po(3, (mod - 1) / m), ib = po(b, mod - 2);
		w[m / 2] = iw[m / 2] = 1;
		for (int i = 1; i < m / 2; ++i) 
			w[m / 2 + i] = mul(w[m / 2 + i - 1], b), iw[m / 2 + i] = mul(iw[m / 2 + i - 1], ib);
		for (int i = m / 2 - 1; i; --i) 
			w[i] = w[i << 1], iw[i] = iw[i << 1];
	}
	void ntt(int* a, int f)
	{
		for (int i = 0; i < m; ++i)
		{
			if (i < r[i]) 
				swap(a[i], a[r[i]]);
		}
		for (int i = 1, id = 1; i < m; i <<= 1, ++id)
		{
			for (int j = 0; j < m; j += i << 1)
			{
				for (int k = 0; k < i; ++k)
				{
					int x = a[j + k], y = mul((f == 1 ? w[i + k] : iw[i + k]), a[i + j + k]);
					a[j + k] = (x + y >= mod ? x + y - mod : x + y);
					a[i + j + k] = (x - y < 0 ? x - y + mod : x - y);
				}
			}
		}
		if (f == -1)
		{
			int in = po(m, mod - 2);
			for (int i = 0; i < m; ++i) a[i] = mul(a[i], in);
		}
	}
	void mult()
	{
		ntt(a, 1), ntt(b, 1);
		for (int i = 0; i < m; ++i)
			a[i] = mul(a[i], b[i]);
		ntt(a, -1);
	}
}

signed main()
{
	IOS;
	cin >> n >> kk >> t;
	int len = kk.length();
	k = 0;
	for (int i = 0; i < len; i++)
		k = (mul(k, 10) + kk[i] - '0') % mod;
	cout << k << endl;
	for (int i = 1; i <= n; i++)
		cin >> w[i];
	w[0] = 0;
	inv[1] = 1;
	for (int i = 2; i <= n + 5; i++)
	{
		int j = mod % i;
		inv[i] = (-inv[j] * (mod / i) % mod + mod) % mod;
	}
	poly::init(n + 1);
	for (int i = 0; i <= n; i++)
		poly::a[i] = w[i];
	if (t == 0)
	{
		poly::b[0] = 1;
		for (int i = 1; i <= n; i++)
			poly::b[i] = mul(mul(poly::b[i - 1], (((i + k) % mod - 1) % mod + mod) % mod), inv[i]);
		poly::mult();
	}
	else
	{
		poly::b[0] = 1;
		for (int i = 1; i <= n; i++)
			poly::b[i] = mul(mul(poly::b[i - 1], (((k + 1) % mod - i) % mod + mod) % mod), inv[i]);
		for (int i = 1; i <= n; i += 2)
			poly::b[i] = mod - poly::b[i];
		poly::mult();
	}
	for (int i = 1; i <= n; i++)
		cout << poly::a[i] << ' ';
	cout << endl;

	return 0;
}

P4238 【模板】多项式乘法逆

题意

给定 \(F(x)\) ,求满足 \(F(x)G(x)\equiv1(mod\space x^n)\)\(G(x)\)

题解

对式子转化一下,即 \(F(x)-\frac{1}{G(x)}\equiv0(mod\space x^n)\) ,记 \(H(G(x))=F(x)-\frac{1}{G(x)}\) ,与是转化为求函数零点,使用牛顿迭代法,设 \(H(G_0(x))\equiv0(mod\space x^\frac{n}{2})\) , 则 \(G(x)=G_0(x)-\frac{H(G_0(x))}{H'(G_0(x))}\) ,于是 \(G(x)=G_0(x)-\frac{H(G_0(x))}{\frac{1}{G_0(x)^2}}=2G_0(x)-G_0(x)^2F(x)\),递归求解即可, \(n=1\)\(G(x)=\frac{1}{F_0}\) ,复杂度 \(O(nlogn)\)

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
//#define lc p*2
//#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 300010;

LL po(LL x, LL y)
{
	if (y < 0)
		return 0;
	LL r = 1;
	for (; y; y >>= 1, x = mul(x, x))
	{
		if (y & 1)
			r = mul(r, x);
	}
	return r;
}

LL n, F[maxn], G[maxn], inv[maxn], w[maxn];


namespace NTT
{
	LL rev[maxn], a[maxn], b[maxn], w[maxn], iw[maxn], len, l;
	void init(LL x, LL y)//次数
	{
		for (len = 1, l = 0; len <= x + y; len <<= 1, ++l)
			continue;
		for (LL i = 0; i < len; ++i)
			rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (l - 1));
		LL b = po(3, (mod - 1) / len), ib = po(b, mod - 2);
		w[len / 2] = iw[len / 2] = 1;
		for (LL i = 1; i < len / 2; ++i)
			w[len / 2 + i] = mul(w[len / 2 + i - 1], b), iw[len / 2 + i] = mul(iw[len / 2 + i - 1], ib);
		for (LL i = len / 2 - 1; i; --i)
			w[i] = w[i << 1], iw[i] = iw[i << 1];
	}
	void ntt(LL* a, LL f)
	{
		for (LL i = 0; i < len; ++i)
		{
			if (i < rev[i])
				swap(a[i], a[rev[i]]);
		}
		for (LL i = 1, id = 1; i < len; i <<= 1, ++id)
		{
			for (LL j = 0; j < len; j += i << 1)
			{
				for (LL k = 0; k < i; ++k)
				{
					LL x = a[j + k], y = mul((f == 1 ? w[i + k] : iw[i + k]), a[i + j + k]);
					a[j + k] = (x + y >= mod ? x + y - mod : x + y);
					a[i + j + k] = (x - y < 0 ? x - y + mod : x - y);
				}
			}
		}
		if (f == -1)
		{
			LL in = po(len, mod - 2);
			for (LL i = 0; i < len; ++i)
				a[i] = mul(a[i], in);
		}
	}
	void solve(LL x, LL y)
	{
		init(x, y), ntt(a, 1), ntt(b, 1);
		for (LL i = 0; i < len; ++i)
			a[i] = mul(a[i], b[i]);
		ntt(a, -1);
	}
}

namespace INV
{
	LL A[maxn], B[maxn], S[maxn];
	void solve(LL* f, LL* s, LL n)
	{
		S[0] = po(f[0], mod - 2), S[1] = 0;
		for (LL len = 2; len <= (n << 1LL); len <<= 1ll)
		{
			LL lim = len << 1ll;//len/2*2+len
			for (LL i = 0; i < len; i++)
				A[i] = f[i], B[i] = S[i];
			for (LL i = len; i < lim; i++)
				A[i] = B[i] = 0;
			NTT::init(len, 0), NTT::ntt(A, 1), NTT::ntt(B, 1);
			for (LL i = 0; i < lim; i++)
				S[i] = ((2ll * B[i] % mod - B[i] * B[i] % mod * A[i] % mod) % mod + mod) % mod;
			NTT::ntt(S, -1);
			for (LL i = len; i < lim; i++)//mod x^len
				S[i] = 0;
		}
		for (LL i = 0; i <= n; i++)
			s[i] = S[i];
	}
}

void solve()
{
	INV::solve(F, G, n - 1);
	for (LL i = 0; i < n; i++)
		cout << G[i] << ' ';
	cout << endl;
}

int main()
{
	IOS;
	cin >> n;
	for (LL i = 0; i < n; i++)
		cin >> F[i];
	solve();

	return 0;
}

P4841 [集训队作业2013]城市规划

题意

求出 \(n\) 个点的简单 (无重边无自环) 有标号无向连通图数目。

题解

\(i\) 个点的无向连通图数量为 \(g_i\)\(\binom{i}{2}\) 条边都可以选或不选,于是 \(g_i=2^{\binom{i}{2}}\) 。记 \(f_i\)\(i\) 个点时的答案。考虑将所有无向图按 \(1\) 号点所在的连通块大小分类,对于 \(1\) 号点所在连通块大小为 \(i\) 时,从 \(n-1\) 个点中选 \(i-1\) 个点,于是这 \(i\) 个点内部的方案即为 \(f_i\) ,该连通块内的点与其他非连通块内点的连边都只有不选一种可能,然后剩下的其他点内部连接方法就是无向图的数量,于是得到 \(\binom{n-1}{i-1}\cdot f_i\cdot g_{n-i}\) 。于是可以得到

\[g_n=\sum_{i=1}^n\binom{n-1}{i-1}\cdot f_i\cdot g_{n-i} \]

将该式进一步转化得到

\[\frac{2^{\binom{n}{2}}}{(n-1)!}=\sum_{i=1}^n\frac{f_i}{(i-1)!}\cdot\frac{2^{\binom{n-i}{2}}}{(n-i)!} \]

\(a_i=\frac{f_i}{i!}\) ,其OGF记为 \(A(x)\)\(b_i=\frac{2^{\binom{i}{2}}}{i!}\) ,其OGF记为 \(B(x)\) ,于是有 \(xB'(x)=xA'(x)B(x)\) ,解出 \(A'(x)=\frac{B'(x)}{B(x)}\) ,即 \(A(x)=ln(B(x))\) 。最后 \([x^n]A(x)\cdot n!\) 即为答案,复杂度 \(O(nlogn)\)

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
//#define lc p*2
//#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 1004535809;
const int maxn = 300010;

LL qpow(LL x, LL y)
{
	if (y < 0)
		return 0;
	LL r = 1;
	for (; y; y >>= 1, x = mul(x, x))
	{
		if (y & 1)
			r = mul(r, x);
	}
	return r;
}

namespace NTT
{
	LL rev[maxn], a[maxn], b[maxn], w[maxn], iw[maxn], len, l;
	LL init(LL x, LL y)//次数
	{
		for (len = 1, l = 0; len <= x + y; len <<= 1, ++l)
			continue;
		for (LL i = 0; i < len; ++i)
			rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (l - 1));
		LL b = qpow(3, (mod - 1) / len), ib = qpow(b, mod - 2);
		w[len / 2] = iw[len / 2] = 1;
		for (LL i = 1; i < len / 2; ++i)
			w[len / 2 + i] = mul(w[len / 2 + i - 1], b), iw[len / 2 + i] = mul(iw[len / 2 + i - 1], ib);
		for (LL i = len / 2 - 1; i; --i)
			w[i] = w[i << 1], iw[i] = iw[i << 1];
		return len;
	}
	void ntt(LL* a, LL f)
	{
		for (LL i = 0; i < len; ++i)
		{
			if (i < rev[i])
				swap(a[i], a[rev[i]]);
		}
		for (LL i = 1, id = 1; i < len; i <<= 1, ++id)
		{
			for (LL j = 0; j < len; j += i << 1)
			{
				for (LL k = 0; k < i; ++k)
				{
					LL x = a[j + k], y = mul((f == 1 ? w[i + k] : iw[i + k]), a[i + j + k]);
					a[j + k] = (x + y >= mod ? x + y - mod : x + y);
					a[i + j + k] = (x - y < 0 ? x - y + mod : x - y);
				}
			}
		}
		if (f == -1)
		{
			LL in = qpow(len, mod - 2);
			for (LL i = 0; i < len; ++i)
				a[i] = mul(a[i], in);
		}
	}
	void solve(LL* f, LL* g, LL* s, LL x, LL y)//f*g=s
	{
		init(x, y);
		for (LL i = 0; i < len; i++)
			a[i] = f[i], b[i] = g[i];
		ntt(a, 1), ntt(b, 1);
		for (LL i = 0; i < len; ++i)
			a[i] = mul(a[i], b[i]);
		ntt(a, -1);
		for (LL i = 0; i < len; i++)
			s[i] = a[i];
	}
}

namespace INV
{
	LL A[maxn], B[maxn], S[maxn];
	void solve(LL* f, LL* s, LL n)
	{
		S[0] = qpow(f[0], mod - 2), S[1] = 0;
		for (LL len = 2; len <= (n << 1LL); len <<= 1ll)
		{
			LL lim = len << 1ll;//len/2*2+len
			for (LL i = 0; i < len; i++)
				A[i] = f[i], B[i] = S[i];
			for (LL i = len; i < lim; i++)
				A[i] = B[i] = 0;
			NTT::init(len, 0), NTT::ntt(A, 1), NTT::ntt(B, 1);
			for (LL i = 0; i < lim; i++)
				S[i] = ((2ll * B[i] % mod - B[i] * B[i] % mod * A[i] % mod) % mod + mod) % mod;
			NTT::ntt(S, -1);
			for (LL i = len; i < lim; i++)//mod x^len
				S[i] = 0;
		}
		for (LL i = 0; i <= n; i++)
			s[i] = S[i];//S:G_0
	}
}

namespace poly
{
	void Add(LL* f, LL* g, LL* s, LL n, LL m)
	{
		for (LL i = 0; i <= max(n, m); i++)
			s[i] = (f[i] + g[i]) % mod;
	}
	void Del(LL* f, LL* g, LL* s, LL n, LL m)
	{
		for (LL i = 0; i <= max(n, m); i++)
			s[i] = (f[i] + mod - g[i]) % mod;
	}
	void Mult(LL* f, LL* s, LL n, LL k)
	{
		for (LL i = 0; i <= n; i++)
			s[i] = (f[i] * k % mod + mod) % mod;
	}
	//微分
	void Deriv(LL* f, LL* s, LL n)
	{
		static LL A[maxn];
		A[n] = 0;
		for (LL i = 1; i <= n; i++)
			A[i - 1] = f[i] * i % mod;
		for (LL i = 0; i <= n; i++)
			s[i] = A[i];
	}
	//积分
	void Limit(LL* f, LL* s, LL n)
	{
		static LL A[maxn];
		A[0] = 0;
		for (LL i = 0; i <= n - 1; i++)
			A[i + 1] = f[i] * qpow(i + 1, mod - 2) % mod;
		for (LL i = 0; i <= n; i++)
			s[i] = A[i];
	}
	//对数函数
	void Ln(LL* f, LL* s, LL n)
	{
		static LL A[maxn], B[maxn];
		for (LL i = 0; i <= n; i++)
			A[i] = f[i], B[i] = 0;
		LL len = NTT::init(n, n);
		for (LL i = n + 1; i < len; i++)
			A[i] = B[i] = 0;
		INV::solve(A, B, n);
		Deriv(A, A, n);
		NTT::solve(A, B, A, n, n);
		Limit(A, A, n);
		for (LL i = 0; i <= n; i++)
			s[i] = A[i];
	}
	//指数函数
	void Exp(LL* f, LL* s, LL n)
	{
		static LL A[maxn], B[maxn], C[maxn], S[maxn];
		S[0] = 1, S[1] = 0;
		for (LL len = 2; len <= (n << 1LL); len <<= 1ll)
		{
			LL lim = len << 1ll;//len/2*2+len
			Ln(S, C, len - 1);
			for (LL i = 0; i < len; i++)
				A[i] = f[i], B[i] = S[i];
			for (LL i = len; i < lim; i++)
				A[i] = B[i] = C[i] = 0;
			for (LL i = 0; i < len; i++)
				A[i] = ((A[i] - C[i]) % mod + mod) % mod;
			A[0] = (A[0] + 1) % mod;
			NTT::init(len, 0), NTT::ntt(A, 1), NTT::ntt(B, 1);//(len,lim]
			for (LL i = 0; i < lim; i++)
				S[i] = A[i] * B[i] % mod;
			NTT::ntt(S, -1);
			for (LL i = len; i < lim; i++)//mod x^len
				S[i] = 0;
		}
		for (LL i = 0; i <= n; i++)
			s[i] = S[i];//S:G_0
	}
	//除法(取模)
	void Rev(LL* f, LL* s, LL n)//系数翻转
	{
		for (LL i = 0; i <= n / 2; i++)
			swap(f[i], f[n - i]);
	}
	void Div(LL* f, LL* g, LL* q, LL* r, LL n, LL m)//F=G*Q+R
	{
		static LL A[maxn], B[maxn], C[maxn], D[maxn];
		for (LL i = 0; i <= n; i++)
			A[i] = C[i] = f[i];
		for (LL i = 0; i <= m; i++)
			B[i] = D[i] = g[i];
		Rev(A, A, n), Rev(B, B, m);
		INV::solve(B, B, n - m);
		for (LL i = n - m + 1; i <= m; i++)
			B[i] = 0;
		NTT::solve(A, B, q, n, n - m);
		for (LL i = n - m + 1; i <= n + n - m; i++)
			q[i] = 0;
		Rev(q, q, n - m);
		NTT::solve(D, q, D, m, n - m);
		Del(C, D, r, n, n);
	}
}

LL n, m, F[maxn], G[maxn], fac[maxn], invf[maxn];

void solve()
{
	fac[0] = fac[1] = 1;
	for (LL i = 2; i <= 150000; i++)
		fac[i] = i * fac[i - 1] % mod;
	invf[150000] = qpow(fac[150000], mod - 2);
	for (LL i = 150000; i >= 1; i--)
		invf[i - 1] = invf[i] * i % mod;
	for (LL i = 0; i <= n; i++)
		F[i] = qpow(2LL, max(0LL, i * (i - 1) / 2)) * invf[i] % mod;
	poly::Ln(F, G, n);
	cout << G[n] * fac[n]%mod << endl;
}

int main()
{
	IOS;
	cin >> n;
	solve();

	return 0;
}

CF438E. The Child and Binary Tree

\(\texttt{*3100}\)

题意

\(n(1\le n\le10^5)\) 个不同的整数 \(c_i(1\le c_i\le10^5)\) 组成集合 \(C\) ,每个节点权值 \(\in C\) 对所有 \(1\le s\le m\) ,计算有多少种不同的二叉树,使得总权值和为 \(s\)

题解

考虑 \(dp\) ,记 \(f_n\) 为总权值为 \(n\) 时的答案,枚举根的权值及左子树权值和,有

\[f_n=\sum_{x\in C}\sum_{i=0}^nf_i\cdot f_{n-i-x} \]

\[f_0=1 \]

考虑写成卷积形式,记 \(F(x)=\sum_nf_nx^n\)\(G(x)=\sum_{n\in C}x^n\) ,于是有

\[F(x)=G(x)F(x)^2+1 \]

解出 \(F(x)\) 并且分母有理化之后,得到 \(F(x)=\frac{2}{1\pm\sqrt{1-4G(x)}}\)
代入 \(f_0\) 得到取正号,之后套板子即可, \(O(nlogn)\)

代码

省略板子部分

view code
LL N, M, F[maxn], G[maxn], H[maxn];

void solve()
{
	poly::Sqrt(G, H, max(N, M));
	H[0] = (H[0] + 1) % mod;
	INV::solve(H, F, max(N, M));
	for (LL i = 1; i <= M; i++)
		cout << F[i] * 2 % mod << endl;
}

int main()
{
	IOS;
	cin >> N >> M;
	int c;
	for (int i = 1; i <= N; i++)
		cin >> c, G[c] = -4;
	G[0] = 1;
	solve();

	return 0;
}

CF1729G. Cut Substrings

\(\texttt{*2100}\)

题意

给串 \(s,t\) ,每次可以将 \(s\) 中的任意子串 \(t\) 替换为 \(...\) ,求最少操作多少次使得 \(s\) 中没有子串 \(t\) ,并求出最少操作的方案数。
\(1\le|s|,|t|\le500\)

题解

一眼 \(O(n^3)dp\) ,预处理处 \(s\) 中所有子串 \(t\) 的起始位置 \(p\) ,记 \(f_i\) 为删除了 \(p_i\) ,并且其前面所有 \(p\) 都删除时的最小次数,记 \(m=|t|\) ,注意到删除 \(p_i\) 后,\([p_i-m+1,p_i+m-1]\) 内的所有 \(p\) 也被删除,于是对于 \(f_i\) ,枚举上一个被删除的 \(p_j\) ,检查 \((p_j,p_i)\) 中是否有点会删不掉,如果不会,转移 \(p_i=min(p_j+1,p_i)\) ,注意边界条件,答案按照上述方法枚举一下,计数的话正着再推一遍即可。
想对了一开始,不知道为啥觉得自己假了。。。

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
//#define lc p*2
//#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 100010;
const LL inv2 = (mod + 1) / 2;

LL Q, f[510], p[510], g[510], tot = 0;
string S, T;

void solve()
{
	int n = S.length(), m = T.length();
	S = ' ' + S, T = ' ' + T;
	for (int i = 1; i + m - 1 <= n; i++)
	{
		bool flag = true;
		for (int j = 1, k = i; j <= m; j++, k++)
		{
			if (S[k] != T[j])
			{
				flag = false;
				break;
			}
		}
		if (flag)
			p[++tot] = i;
	}
	f[1] = 1;
	for (int i = 2; i <= tot; i++)
	{
		for (int j = 1; p[j] + m - 1 < p[i]; j++)
		{
			bool flag = true;
			for (int k = j + 1; p[k] + m - 1 < p[i]; k++)
			{
				if (p[k] > p[j] + m - 1 && p[k] < p[i] - m + 1)
				{
					flag = false;
					break;
				}
			}
			if (flag)
				f[i] = min(f[i], f[j] + 1);
		}
		if (p[i] - m + 1 <= p[1])
			f[i] = min(f[i], 1ll);
	}
	LL ans1 = INF, ans2 = 0;
	for (int i = 1; i <= tot; i++)
	{
		if (p[tot] <= p[i] + m - 1)
			ans1 = min(ans1, f[i]);
	}
	if (ans1 == INF)
		ans1 = 0;
	for (int i = 1; i <= tot; i++)
	{
		if (f[i] == 1)
		{
			g[i] = 1;
			continue;
		}
		for (int j = 1; p[j] + m - 1 < p[i]; j++)
		{
			bool flag = true;
			for (int k = j + 1; p[k] + m - 1 < p[i]; k++)
			{
				if (p[k] > p[j] + m - 1 && p[k] < p[i] - m + 1)
				{
					flag = false;
					break;
				}
			}
			if (flag && f[j] + 1 == f[i])
				g[i] = (g[i] + g[j]) % MOD;
		}
	}
	for (int i = 1; i <= tot; i++)
	{
		if (p[tot] <= p[i] + m - 1 && f[i] == ans1)
			ans2 = (ans2 + g[i]) % MOD;
	}
	if (ans1 == 0)
		ans2 = 1;
	cout << ans1 << ' ' << ans2 << endl;
}

int main()
{
	IOS;
	cin >> Q;
	while (Q--)
	{
		mst(f, inf), mst(p, 0), mst(g, 0), tot = 0;
		cin >> S >> T;
		solve();
	}
}

CF1746D. Cut Substrings

\(\texttt{*1900}\)

题意

\(n(1\le n\le2\cdot10^5)\) 个节点 \(1\) 为根的树,有一个从 \(1\) 开始的路径组成的大小为 \(k(1\le k\le10^9)\) 的多重集,要求对于一个节点的所有儿子,被多重集中路径经过次数 \(c_i\) 相互的差距不超过 \(1\) ,每个节点有权值 \(s_i\) ,求 \(\sum_{i=1}^nc_is_i\) 的最大值。

题解

对于父亲的经过次数 \(c_i\) ,其儿子有 \(x\) 个,那么儿子取值仅可能为 \(\lfloor\frac{c_i}{x}\rfloor\)\(\lfloor\frac{c_i}{x}\rfloor + 1\) 。分别用 \(f_{v,0},f_{v,1}\) 来表示节点 \(v\) 这两种取值时的贡献,容易发现每个节点最多只有这两种可能的取值,转移时对所有儿子,先默认不多拿,同时将多拿一个之后的增量排序,贪心的取前若干个儿子选择他们多拿了之后的贡献,注意转移到自己多拿一个的时候可以多选择一个儿子多拿,然后 \(f_{1,0}\) 即为答案,复杂度 \(O(nlogn)\)

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using PLL = pair<LL, LL>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
//#define lc p*2
//#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 200010;

LL T, N, S[maxn], K, ans = 0, f[maxn][2];
vector<LL>G[maxn];


void add_edge(int from, int to)
{
	G[from].push_back(to);
	G[to].push_back(from);
}

void dfs(LL v, LL p, LL c)
{
	LL s = G[v].size();
	if (v != 1)
		s--;
	f[v][0] = c * S[v], f[v][1] = (c + 1) * S[v];
	if (!s)
		return;
	vector<PLL>tmp;
	LL base = c / s, ex = c % s;
	for (auto& to : G[v])
	{
		if (to == p)
			continue;
		dfs(to, v, base);
	}
	for (auto& to : G[v])
	{
		if (to == p)
			continue;
		tmp.push_back(PLL(f[to][1] - f[to][0], to));
	}
	sort(all(tmp)), reverse(all(tmp));
	for (int i = 0; i < s; i++)
	{
		if (i < ex)
			f[v][0] += f[tmp[i].second][1];
		else
			f[v][0] += f[tmp[i].second][0];
		if (i <= ex)
			f[v][1] += f[tmp[i].second][1];
		else
			f[v][1] += f[tmp[i].second][0];
	}
}

void solve()
{
	dfs(1, 0, K);
	cout << f[1][0] << endl;
}

int main()
{
	IOS;
	cin >> T;
	while (T--)
	{
		cin >> N >> K;
		for (int i = 1; i <= N; i++)
			G[i].clear(), f[i][0] = f[i][1] = 0;
		int p;
		for (int i = 2; i <= N; i++)
			cin >> p, add_edge(p, i);
		for (int i = 1; i <= N; i++)
			cin >> S[i];
		solve();
	}

	return 0;
}

1436E. Complicated Computations

\(\texttt{*2400}\)

题意

长为 \(n(1\le n\le10^5)\) 的序列 \(a(1\le a_i\le n)\) 。求其所有字段的 \(mex\) 所组成的新序列的 \(mex\) ,该题中 \(mex\) 定义为未出现的最小正整数。

题解

我们考虑从小到大逐一 \(check\) 每个正整数 \(x\) 是否能够作为最终的答案,如果 \(x\) 能作为最终的答案,需要其不是任何一个字段的 \(mex\) ,我们考虑原序列中所有 \(a_i=x\) 的位置,跨过这些位置的字段的 \(mex\) 肯定不为 \(x\) ,于是仅需 \(check\) 被这些位置所划分出的各区间 \(mex\) 是否为 \(x\) 即可,这部分用主席树求区间 \(mex\) 可以解决,单次查询 \(O(logn)\) ,显然对于所有的 \(x\) ,总查询次数为 \(O(n)\) 级别,于是可以 \(O(nlogn)\) 解决问题,注意答案可能的最大值为 \(n+2\)

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using PLL = pair<LL, LL>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
//#define lc p*2
//#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const double pi = acos(-1);
const LL MOD = 10007;
const LL mod = 998244353;
const int maxn = 100010;

string S[210];
int ans[210], cnt = 0, N, A[10010], sum[210];

LL qpow(LL a, LL x)
{
	LL ans = 1;
	while (x)
	{
		if (x & 1)
			ans = ans * a % MOD;
		a = a * a % MOD, x >>= 1;
	}
	return ans;
}

LL inv(LL x)
{
	return qpow(x, MOD - 2);
}

void solve()
{
	int a, b;
	cnt--;
	istringstream strm(S[cnt]);
	strm >> a >> b;
	sum[cnt - 1] = b, ans[cnt - 1] = ((a - b) % MOD + MOD) % MOD;
	for (int i = cnt - 1; i >= 2; i--)
	{
		istringstream strm(S[i]);
		sum[i - 1] = ((sum[i] - ans[i]) % MOD + MOD) % MOD, strm >> N;
		N = ((N - sum[i - 1]) % MOD + MOD) % MOD;
		int len = 0;
		while (strm)
			strm >> A[++len];
		len--, ans[i - 1] = N * inv(len) % MOD;
	}
	for (int i = 1; i < cnt; i++)
		cout << ans[i] << endl;
}

int main()
{
	IOS;
	while (getline(cin, S[++cnt]))
		;
	solve();

	return 0;
}

(3-9)1753C. Wish I Knew How to Sort

\(\texttt{*2000}\)

题意

长为 \(n(1\le n\le2\cdot10^5)\) 的01串,每次操作选择一对下标 \((i,j)(i<j)\) ,如果 \((a_i>a_j)\) 则交换,否则什么也不做,但仍算作一次操作。求使得该序列有序的期望操作数, \(mod\ 998244353\)

题解

问题等价于让所有的 \(1\) 都移动到右侧长为 \(cnt_1\) 的后缀中,已经在后缀中的 \(1\) 显然永远不会被移除,考虑当前右侧还有 \(i\)\(0\) 时减少一个 \(0\) 的概率,显然为 \(\frac{i^2}{\binom{n}{2}}\) ,由于期望是概率的倒数,因此答案为 \(\sum_1^{cnt_0}\frac{\binom{n}{2}}{i^2}\)

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using PLL = pair<LL, LL>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
//#define lc p*2
//#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const double pi = acos(-1);
const LL MOD = 10007;
const LL mod = 998244353;
const int maxn = 200010;

LL T, N, A[maxn];

LL qpow(LL a, LL x)
{
	LL ans = 1;
	while (x)
	{
		if (x & 1)
			ans = ans * a % mod;
		a = a * a % mod, x >>= 1;
	}
	return ans;
}

void solve()
{
	LL ans = 0, cnt1 = 0, cnt0 = 0;
	for (int i = 1; i <= N; i++)
		cnt1 += (A[i] == 1);
	for (int i = N - cnt1 + 1; i <= N; i++)
		cnt0 += (A[i] == 0);
	for (LL i = 1; i <= cnt0; i++)
		ans = (ans + N * (N - 1) / 2LL % mod * qpow(i * i, mod - 2) % mod) % mod;
	cout << ans << endl;
}

int main()
{
	IOS;
	cin >> T;
	while (T--)
	{
		cin >> N;
		for (int i = 1; i <= N; i++)
			cin >> A[i];
		solve();
	}

	return 0;
}

(3-10)510E. Fox And Dinner

\(\texttt{*2300}\)

题意

\(n(3\le n200)\) 只狐狸,每只年龄 \(a_i(2\le a_i\le10^4)\) 。需要安排所有狐狸坐成若干个圆桌,每只狐狸与其左侧狐狸,右侧狐狸年龄的和均为质数,求方案或给出无解。

题解

由于年龄 \(>2\) ,两个狐狸可以相邻必然一奇一偶,于是按奇偶构成二分图,如果相加为质数则连边,原题即需要各狐狸恰好匹配两个其他狐狸,于是我们用网络流跑各点最大匹配数为 \(2\) 的二分图匹配,如果流量不等于 \(n\) 则无解,否则 \(dfs\) 不断找满流的边即可构造出答案。

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using PLL = pair<LL, LL>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
//#define lc p*2
//#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 510;
const int maxm = 250010;
const LL inv2 = (mod + 1) / 2;
struct edge {
	int to, cap, rev;
};
vector<edge> G[210];
vector<int>ans[210];
int N, A[210], level[210], iter[210], cnt = 0;
bool vis[maxn];

void add_edge(int from, int to, int cap)
{
	G[from].push_back(edge{ to,cap,(int)G[to].size() });
	G[to].push_back(edge{ from,0,(int)G[from].size() - 1 });
}

void bfs(int s)
{
	mst(level, -1);
	queue<int> que;
	level[s] = 0, que.push(s);
	while (!que.empty())
	{
		int v = que.front();
		que.pop();
		for (int i = 0; i < G[v].size(); i++)
		{
			edge& e = G[v][i];
			if (e.cap > 0 && level[e.to] < 0)
				level[e.to] = level[v] + 1, que.push(e.to);
		}
	}
}

int dfs(int v, int t, int f)
{
	if (v == t)
		return f;
	for (int& i = iter[v]; i < G[v].size(); i++)
	{
		edge& e = G[v][i];
		if (e.cap > 0 && level[v] < level[e.to])
		{
			int d = dfs(e.to, t, min(f, e.cap));
			if (d > 0)
			{
				e.cap -= d, G[e.to][e.rev].cap += d;
				return d;
			}
		}
	}
	return 0;
}

int max_flow(int s, int t)
{
	int flow = 0;
	while (true)
	{
		bfs(s);
		if (level[t] < 0)
			return flow;
		mst(iter, 0);
		int f;
		while ((f = dfs(s, t, INF)) > 0)
			flow += f;
	}

	return flow;
}

bool isprime(int x)
{
	if (x < 2)
		return false;
	for (int i = 2; i * i <= x; i++)
	{
		if (x % i == 0)
			return false;
	}
	return true;
}

void dfs2(int v, int id, int t)
{
	vis[v] = true, ans[id].push_back(v);
	for (auto& [to, cap, rev] : G[v])
	{
		if (vis[to])
			continue;
		if (t)
		{
			if (cap == 0)
				dfs2(to, id, t ^ 1);
		}
		else
		{
			if (G[to][rev].cap == 0)
				dfs2(to, id, t ^ 1);
		}
	}
}

void solve()
{
	int S = N + 1, T = S + 1;
	for (int i = 1; i <= N; i++)
	{
		for (int j = i + 1; j <= N; j++)
		{
			if (isprime(A[i] + A[j]))
			{
				if (A[i] & 1)
					add_edge(i, j, 1);
				else
					add_edge(j, i, 1);
			}
		}
	}
	for (int i = 1; i <= N; i++)
	{
		if (A[i] & 1)
			add_edge(S, i, 2);
		else
			add_edge(i, T, 2);
	}
	int f = max_flow(S, T);
	if (f != N)
		cout << "Impossible" << endl;
	else
	{
		vis[S] = vis[T] = true;
		for (int i = 1; i <= N; i++)
		{
			if ((A[i] & 1) && !vis[i])
				dfs2(i, ++cnt, 1);
		}
		cout << cnt << endl;
		for (int i = 1; i <= cnt; i++)
		{
			cout << ans[i].size() << ' ';
			for (auto& c : ans[i])
				cout << c << ' ';
			cout << endl;
		}
	}
}

int main()
{
	IOS;
	cin >> N;
	for (int i = 1; i <= N; i++)
		cin >> A[i];
	solve();

	return 0;
}
posted @ 2022-09-22 10:09  Prgl  阅读(26)  评论(0)    收藏  举报