2022牛客多校 补题

1

赛中通过:A,D,G,I

C. Grab the Seat!

题目大意

一个二维平面,屏幕是 \((0, 1)\sim(0, m)\) 的线段
\(n\)\(m(1\le n,m\le 2\cdot10^5)\) 列座位在屏幕前面,是坐标范围 \(1 ≤ x ≤ n, 1 ≤ y ≤ m\) 的整点
最开始有有 \((1\le k\le 2\cdot 10^5)\) 个座位已经有人,求出到屏幕的视线不被任何人挡住的座位数量
\(q(1\le q\le200)\) 次询问,每次修改一个人的坐标后求出答案

思路

考虑每个有人的座位是如何挡住其他座位的,画图后容易发现,其挡住的是其后面的呈三角形的一片区域,并且可以发现,一行中没有被挡的座位是一个前缀,于是我们在每行求出最后一个未被挡住的即可,我们分别处理从 \((0,1)\) 出发的直线,在扫描的过程中,我们始终考虑斜率最大的直线,因为小的影响被覆盖了,求一下交点判断一下即可,从 \((0,m)\) 出发的类似可得,最后每行取个 \(min\) ,加起来就是答案,复杂度 \(O(nq)\) 。其他具体细节见代码。

代码

#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<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 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;

int N, M, K, Q, mi[maxn], cnt[maxn];
PII S[maxn];

void solve()
{
	int p, x, y;
	for (int i = 1; i <= Q; i++)
	{
		LL ans = 0;
		cin >> p >> x >> y;
		S[p] = PII(x, y);
		for (int j = 1; j <= M; j++)
			mi[j] = N + 1, cnt[j] = INF;
		for (int j = 1; j <= K; j++)
			mi[S[j].second] = min(mi[S[j].second], S[j].first);
		cnt[1] = mi[1] - 1;
		LL ky = 0, kx = mi[1], tmpy, tmpx;
		for (int j = 2; j <= M; j++)
		{
			if (mi[j] < N + 1)
			{
				tmpy = j - 1, tmpx = mi[j];
				if (tmpx * ky < tmpy * kx)
					ky = tmpy, kx = tmpx;
			}
			if (ky != 0)
			{
				LL tmp = ((j - 1) * kx) / ky;
				if (((j - 1) * kx) % ky == 0)
					tmp--;
				cnt[j] = tmp;
			}
		}
		cnt[M] = min(cnt[M], mi[M] - 1);
		ky = 0, kx = mi[M];
		for (int j = M - 1; j >= 1; j--)
		{
			if (mi[j] < N + 1)
			{
				tmpy = j - M, tmpx = mi[j];
				if (tmpx * ky > tmpy * kx)
					ky = tmpy, kx = tmpx;
			}
			if (ky != 0)
			{
				LL tmp = (-(j - M) * kx) / -ky;
				if ((-(j - M) * kx) % -ky == 0)
					tmp--;
				cnt[j] = min(cnt[j], tmp);
			}
		}
		for (int j = 1; j <= M; j++)
		{
			if (cnt[j] < 0)
				ans += 0;
			else if (cnt[j] > N)
				ans += N;
			else
				ans += cnt[j];
		}
		cout << ans << endl;
	}
}

signed main()
{
	IOS;
	cin >> N >> M >> K >> Q;
	for (int i = 1; i <= K; i++)
		cin >> S[i].first >> S[i].second;
	solve();

	return 0;
}

J.

待补

H.

待补

2

E. Falfa with Substring

题目大意

一个由小写字母组成的长为 \(n(1\le n\le10^6)\) 的串,求子串 \(bit\) 分别出现 \(0\sim n\) 次的不同字符串个数。

思路

考虑记钦点子串 \(bit\) 出现 \(x\) 次时的个数 \(f(x)=\binom{n-2x}{x}26^{n-3x}\) 。根据二项式反演有恰好出现 \(n\) 次时的个数 \(g(x)=\sum_{i=x}^n(-1)^{i-x}\binom{i}{x}f(i)\) 。暴力算复杂度为 \(O(n^2)\) ,于是我们考虑卷积,\(c_{n-i}=\sum_{j=0}^{n-i}a_jb_{n-i-j}\) 。设 \(a_j=(n-j)!f(n-j)\)\(b_j=\frac{(-1)^j}{j!}\) 。于是 \(g(x)=\sum_{i=x}^n(-1)^{i-x}\binom{i}{x}f(i)=\sum_{i=x}^n(-1)^{i-x}\frac{i!}{(i-x)!x!}f(i)=\sum_{i=0}^{n-x}(-1)^{n-i-x}\frac{(n-i)!}{(n-i-x)!x!}f(n-i)=\frac{c_{n-x}}{x!}\) 。于是只要算出 \(c_{n-x}\) 即可,可以用 \(\texttt{NTT}\)\(O(nlogn)\) 算出所有的 \(c\)
二项式反演的卷积形式:

\[c_{n-x}=g_xx! \]

\[a_{n-i}=i!f(i) \]

\[b_{i}=\frac{(-1)^i}{i!} \]

\[g_xx!=\sum_{i=0}^n\frac{(-1)^{i-x}}{(i-x)!}i!f_i\to c_{n-x}=\sum_{i=0}^nb_{i-x}a_{n-i} \]

比牛逼队友写的慢了 \(3\) 倍,合理怀疑这 \(\texttt{NTT}\) 板子常数太大了。

代码

#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 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 maxm = 200010;
const int maxn = 4000010;

LL N, S[maxn], fact[maxn], invfact[maxn];

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

	return ans % m;
}

void fact_init(LL n, LL m)
{
	fact[0] = fact[1] = 1;
	for (LL i = 2; i <= n; i++)
		fact[i] = fact[i - 1] * i % m;
	invfact[n] = qpow(fact[n], m - 2, m);
	for (LL i = n; i > 0; i--)
		invfact[i - 1] = invfact[i] * i % m;
}

LL C(LL x, LL y, LL m)
{
	return fact[x] * invfact[y] % m * invfact[x - y] % m;
}

const LL G = 3;
LL g[maxn], invg[maxn];

void init(const int& n)
{
	for (int i = 0; i < n; i++)
	{
		g[i] = qpow(G, (mod - 1) / n * i, mod);
		invg[i] = qpow(g[i], mod - 2, mod);
	}
}

void transform(LL* a, const int& n, const LL* g)
{
	for (int i = 0, j = 0; i < n; i++)
	{
		if (i > j)
			swap(a[i], a[j]);
		for (int l = n >> 1; (j ^= l) < l; l >>= 1)
			continue;
	}
	for (int l = 2; l <= n; l <<= 1)
	{
		int m = l / 2;
		for (LL* p = a; p != a + n; p += l)
		{
			for (int i = 0; i < m; i++)
			{
				LL t = g[n / l * i] * p[m + i] % mod;
				p[m + i] = (p[i] - t + mod) % mod;
				p[i] = (p[i] + t) % mod;
			}
		}
	}
}

void DFT(LL* a, const int& n)
{
	transform(a, n, g);
}

void IDFT(LL* a, const int& n)
{
	transform(a, n, invg);
	LL invn = qpow(n, mod - 2, mod);
	for (int i = 0; i < n; i++)
		a[i] = a[i] * invn % mod;
}

void NTT(LL* a, LL* b, LL* c, const int& n)
{
	DFT(a, n), DFT(b, n);
	for (int i = 0; i < n; i++)
		c[i] = a[i] * b[i] % mod;
	IDFT(c, n);
}

LL A[maxn], B[maxn], D[maxn];

void solve()
{
	for (LL i = N; i >= 0; i--)
	{
		A[N - i] = C(N - i * 2, i, mod) * qpow(26, N - i * 3, mod) % mod * fact[i] % mod;
		B[i] = (i % 2 ? mod - invfact[i] : invfact[i]);
	}
	LL K = N * 2 + 1, T = 1;
	while (T < K)
		T <<= 1;
	init(T), NTT(A, B, D, T);
	for (int i = 0; i <= N; i++)
		cout << D[N - i] * invfact[i] % mod << ' ';
	cout << endl;
}

int main()
{
	IOS;
	fact_init(1000005, mod);
	cin >> N;
	solve();

	return 0;
}

题目大意

\(n(1\le n\le10^4)\)# 张地图,每个地图都有 \(m(2\le m\le2\cdot10^3)\) 个节点,\(l(0\le l\le m(m-1))\) 条有向边,可以从任意的 \(1\) 号节点开始,每次在当前地图内走一步或者不动,随后立即传送到下一张地图对应的节点,求存在一种从 \(1\)\(m\) 的方法至少需要多少张连续的地图。

思路

考虑将同一张地图内的有向边 \(u\to v\) 转化为当前地图的 \(u\) 向下一张地图 \(v\) 的连边,对于不动的情况也类似连边,之后我们可以直接在图上从所有的 \(1\) 开始 \(bfs\) ,复杂度为 \(O(nm)\) ,但是显然空间开不下。发现如果每次只跑一张地图,接着再跑下一张,相当于在按这张图的拓扑序在跑,于是我们每次可以只建出当前地图向下一张地图的连边,然后用滚动数组 \(dp\) 求最短路即可,每次跑完一张地图后更新一下最终的答案。
可能需要治一治眼睛,赛时完全没看见 \(\texttt{continuous}\) 这个字。。。

代码

#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 maxm = 2010;

vector<int>G[maxm];
int f[2][maxm], N, M;

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

void solve()
{
	mst(f, inf), f[1][1] = 0;
	int l, u, v, ans = inf;
	for (int i = 1; i <= N; i++)
	{
		for (int j = 1; j <= M; j++)
			G[j].clear();
		cin >> l;
		for (int j = 1; j <= l; j++)
			cin >> u >> v, add_edge(u, v);
		for (int j = 1; j <= M; j++)
		{
			f[(i + 1) % 2][j] = min(f[(i + 1) % 2][j], f[i % 2][j] + 1);
			for (auto& to : G[j])
				f[(i + 1) % 2][to] = min(f[(i + 1) % 2][to], f[i % 2][j] + 1);
		}
		f[(i + 1) % 2][1] = 0, ans = min(ans, f[(i + 1) % 2][M]);
		for (int j = 1; j <= M; j++)
			f[i % 2][j] = inf;
	}
	cout << (ans == inf ? -1 : ans) << endl;
}

int main()
{
	IOS;
	cin >> N >> M;
	solve();

	return 0;
}

9

K. NIO's OAuth2 Server

题意

\(n(1\le n\le10^5)\) 种集合,每种集合有 \(m_i(1\le m_i\le k)\) 种元素,所有元素 \(\in[1,k]\) ,求全集 \(k(1\le k\le 20)\) 的所有非空子集,至少用几个给定的集合,才能或出其超集,求每个答案对应的集合数。

思路

考虑做 \(k\) 次或卷积,记录各个集合最早被卷出来的次数,之后对于每个集合,做高维后缀 \(\texttt{min}\) 即为答案,最后统计数量即可,复杂度 \(O(2^k\cdot k)\)

代码

#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<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 tr[x].ch[0]
//#define rc tr[x].ch[1]
#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-10;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 2000010;

LL N, K, A[maxn], B[maxn], C[maxn], dp[maxn], w[maxn], ans[maxn];
bool vis[maxn];

void SOS()
{
	for (int i = 0; i < (1LL << K); i++)
		dp[i] = w[i];
	for (int i = 0; i < K; i++)
	{
		for (int s = 0; s < (1LL << K); s++)
		{
			if (!((s >> i) & 1))
				dp[s] = min(dp[s], dp[s ^ (1LL << i)]);
		}
	}
}

void FWT_or(LL* f, int t)//t = 1: fwt, t = -1: ifwt
{
	for (int i = 2; i <= (1LL << K); i <<= 1)//2^N
	{
		for (int p = i >> 1, j = 0; j < (1LL << K); j += i)
		{
			for (int k = j; k < j + p; k++)
				f[k + p] += f[k] * t;
		}
	}
}

void solve()
{
	A[0] = B[0] = 1, FWT_or(B, 1);
	for (int j = 1; j < (1LL << K); j++)
		w[j] = INF;
	for (int i = 1; i <= K; i++)
	{
		FWT_or(A, 1);
		for (int j = 0; j < (1LL << K); j++)
			C[j] = A[j] * B[j];
		FWT_or(C, -1);
		for (int j = 0; j < (1LL << K); j++)
		{
			if (C[j])
				A[j] = 1, w[j] = min(w[j], (LL)i);
			else
				A[j] = 0;
		}
	}
	SOS();
	for (int i = 1; i < (1LL << K); i++)
	{
		if (dp[i] == INF)
			continue;
		ans[dp[i]]++;
	}
	for (int i = 1; i <= K; i++)
		cout << ans[i] << ' ';
	cout << endl;
}

signed main()
{
	IOS;
	cin >> N >> K;
	LL m, x;
	for (int i = 1; i <= N; i++)
	{
		cin >> m;
		LL tmp = 0;
		for (int j = 1; j <= m; j++)
			cin >> x, tmp |= (1LL << (x - 1));
		B[tmp] = 1;
	}
	solve();

	return 0;
}
posted @ 2022-07-22 22:01  Prgl  阅读(38)  评论(0)    收藏  举报