"蔚来杯"2022牛客暑期多校训练营9

A.Car Show

给定一个长为\(n\)的序列\(A\),求有多少区间\([L,R]\)包含所有\(1,2,...,m\)


考虑枚举左端点,合法的右端点一定是\([R',n]\)且右端点是严格不减的。移动指针的时候维护出现的数的次数和种数,这样可以在均摊\(O(1)\)的时间内对每个\(L\)求出其相应的\(R\)

/*program from Wolfycz*/
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_set>
#define lMax 1e18
#define MK make_pair
#define iMax 0x7f7f7f7f
#define sqr(x) ((x)*(x))
#define pii pair<int,int>
#define UNUSED(x) (void)(x)
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
template<typename T>inline T read(T x) {
	int f = 1; char ch = getchar();
	for (; ch < '0' || ch>'9'; ch = getchar())	if (ch == '-')	f = -1;
	for (; ch >= '0' && ch <= '9'; ch = getchar())	x = (x << 1) + (x << 3) + ch - '0';
	return x * f;
}
inline void print(int x) {
	if (x < 0)	putchar('-'), x = -x;
	if (x > 9)	print(x / 10);
	putchar(x % 10 + '0');
}
const int N = 1e5;
unordered_set<int>Cars;
int Cnt[N + 10], A[N + 10];
void Add(int x) {
	if (!Cnt[A[x]])
		Cars.insert(A[x]);
	Cnt[A[x]]++;
}
void Del(int x) {
	if (Cnt[A[x]] == 1)
		Cars.erase(A[x]);
	Cnt[A[x]]--;
}
int main() {
	int n = read(0), m = read(0);
	for (int i = 1; i <= n; i++)	A[i] = read(0);
	ll Ans = 0;
	for (int l = 1, r = 0; l <= n; l++) {
		while (r < n && (int)Cars.size() < m)	Add(++r);
		if ((int)Cars.size() == m)
			Ans += n - r + 1;
		Del(l);
	}
	printf("%lld\n", Ans);
	return 0;
}

B.Two Frogs

\(n\)个荷叶,在第\(i(i<n)\)个荷叶上会等概率跳到\((i,i+a_i]\)上,问两只青蛙花费相同的步数跳到\(n\)的概率?


\(F[i][j]\)表示跳\(i\)次恰好跳到\(j\)的概率,显然\(F[i][j]\)\(F[i+1][k](j<k\leqslant j+a_j)\)\(\frac{F[i][j]}{a_j}\)的贡献,差分一下即可

最后答案就是\(\sum\limits_{i=1}^{n-1}F[i][n]^2\)

/*program from Wolfycz*/
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_set>
#define lMax 1e18
#define MK make_pair
#define iMax 0x7f7f7f7f
#define sqr(x) ((x)*(x))
#define pii pair<int,int>
#define UNUSED(x) (void)(x)
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
template<typename T>inline T read(T x) {
	int f = 1; char ch = getchar();
	for (; ch < '0' || ch>'9'; ch = getchar())	if (ch == '-')	f = -1;
	for (; ch >= '0' && ch <= '9'; ch = getchar())	x = (x << 1) + (x << 3) + ch - '0';
	return x * f;
}
inline void print(int x) {
	if (x < 0)	putchar('-'), x = -x;
	if (x > 9)	print(x / 10);
	putchar(x % 10 + '0');
}
const int N = 8e3, P = 998244353;
int Inv[N + 10];
int Pre[N + 10][N + 10];
int main() {
	Inv[1] = 1;
	for (int i = 2; i <= N; i++)	Inv[i] = 1ll * (P - P / i) * Inv[P % i] % P;
	int n = read(0);
	Pre[0][1] = 1, Pre[0][2] = P - 1;
	for (int i = 1; i < n; i++) {
		int x = read(0);
		for (int j = 0; j < i; j++) {
			Pre[j][i] = (Pre[j][i] + Pre[j][i - 1]) % P;
			int temp = 1ll * Pre[j][i] * Inv[x] % P;
			Pre[j + 1][i + 1] = (Pre[j + 1][i + 1] + temp) % P;
			Pre[j + 1][i + 1 + x] = (Pre[j + 1][i + 1 + x] - temp) % P;
		}
	}
	int Ans = 0;
	for (int i = 0; i < n; i++) {
		Pre[i][n] = (Pre[i][n] + Pre[i][n - 1]) % P;
		int temp = Pre[i][n];
		temp = 1ll * temp * temp % P;
		Ans = (Ans + temp) % P;
	}
	printf("%d\n", Ans);
	return 0;
}

C.Global Positioning System

\(n\)个坐标未知的三维点,给出\(m\)对点的相对位置关系,要修改恰好一个关系使得存在一组三维点坐标满足所有关系,找出所有可以被修改的关系(保证有解)


如果存在一组三维点坐标满足所有相对位置关系,当且仅当图上所有回路的矢量和为\(\vec0\)

如果存在回路的矢量和不为\(\vec0\),那么需要被修改的边就是所有这些回路的交集。由于保证有解,交集不会为空,也不需要考虑无法修改一条边使得所有回路的矢量和均为\(\vec0\)的情况

如果所有回路的矢量和均为\(\vec0\),也就是已经有解的情况下,有且只有图中的桥边可以被修改

要判断前述两种情况,只需要任取一棵生成树,考虑所有的非树边与树上路径构成的简单回路即可,这是因为图上所有回路都是这些简单回路的线性组合

这样问题就转化为树上的路径覆盖问题,树上差分即可。这里如果取DFS树作为生成树,所有非树边都是返祖边,就不需要求LCA了

代码?咕咕咕

D.Half Turns

\(n\times m\)的网络图中构造一条恰好有\(\frac{nm}{2}\)次拐弯的哈密顿路径


构造题,代码马上填坑()

E.Longest Increasing Subsequence

构造一个\(1\sim n\)的排列,使其恰好有\(m\)个不同的最长上升子序列。\(m\leqslant 10^9,n\leqslant 100\)


考虑\(m\)的二进制表示\(b_0b_1\cdots b_k\),其中\(b_0=1,b_i\in\{0,1\}\),故我们先构造形如\(2,1,4,3,...,2k,2k-1\)这样一个序列

然后我们再考虑其他位置,如果\(b_i\)等于1,就在原序列第\(2i\)后插入一些数。后插入的数我们假定其从一个极小值开始,但保证按照顺序是递增的。假定在此之前我们已经插入\(a\)个数了,那么我们需要在原序列的第\(2i\)个数之后插入\(i-a\)个数,来保证其使得\(b_i=1\)的同时,满足最长上升子序列长度为\(k\)的限制

最后再离散化一下即可,序列最长在\(3\log_2 m\)左右,不会超过100

/*program from Wolfycz*/
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lMax 1e18
#define MK make_pair
#define iMax 0x7f7f7f7f
#define sqr(x) ((x)*(x))
#define pii pair<int,int>
#define UNUSED(x) (void)(x)
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
template<typename T>inline T read(T x) {
	int f = 1; char ch = getchar();
	for (; ch < '0' || ch>'9'; ch = getchar())	if (ch == '-')	f = -1;
	for (; ch >= '0' && ch <= '9'; ch = getchar())	x = (x << 1) + (x << 3) + ch - '0';
	return x * f;
}
inline void print(int x) {
	if (x < 0)	putchar('-'), x = -x;
	if (x > 9)	print(x / 10);
	putchar(x % 10 + '0');
}
int main() {
	//	freopen(".in","r",stdin);
	//	freopen(".out","w",stdout);
	int T = read(0);
	while (T--) {
		int n = read(0);
		if (n == 1) {
			printf("1\n1\n");
			continue;
		}
		vector<int>digits;
		for (int _n = n; _n; _n >>= 1)
			digits.push_back(_n % 2);
		reverse(digits.begin(), digits.end());
		int len = digits.size() - 1;
		vector<int>Ans, list;
		int Now = -100, Cnt = 0;
		for (int i = 1; i <= len; i++) {
			Ans.push_back(i << 1), Ans.push_back((i << 1) - 1);
			if (!digits[i])	continue;
			while (Cnt < i)	Ans.push_back(Now++), Cnt++;
		}
		list = Ans;
		sort(list.begin(), list.end());
		for (int i = 0; i < (int)Ans.size(); i++)
			Ans[i] = lower_bound(list.begin(), list.end(), Ans[i]) - list.begin();
		printf("%llu\n", Ans.size());
		for (auto p : Ans)
			printf("%d ", p + 1);
		putchar('\n');
	}
	return 0;
}

F.Matrix and GCD

给定\(n\times m\)的矩阵,其中\([1,nm]\)的数字均只出现了一次,问所有连续子矩阵的gcd之和


考虑\(O(n\log n)\)进行倍数枚举,统计\(F[i]\)表示连续子矩阵中全部数字都是\(i\)的倍数的个数,再进行容斥:\(Ans=\sum\limits_{i=1}^{nm}\mu(i)F[i]\)

故我们将问题转化为,\(n\times m\)的矩阵中,有\(k\)个特殊点,问有多少个连续子矩阵是由特殊点构成的。首先对所有特殊点进行\(O(k\log k)\)的排序,然后\(O(k)\)的从下往上统计每个点垂直往下可以延伸多长,再横向两次用单调栈维护它所能往左往右的最远扩张距离。对于这样一整个联通块只需要\(O(k)\)的时间,因而总时间复杂度为\(O(nm\log^2 nm)\)

/*program from Wolfycz*/
#include<map>
#include<set>
#include<cmath>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lMax 1e18
#define MK make_pair
#define iMax 0x7f7f7f7f
#define sqr(x) ((x)*(x))
#define pii pair<int,int>
#define UNUSED(x) (void)(x)
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
template<typename T>inline T read(T x) {
    int f = 1; char ch = getchar();
    for (; ch < '0' || ch>'9'; ch = getchar())	if (ch == '-')	f = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar())	x = (x << 1) + (x << 3) + ch - '0';
    return x * f;
}
inline void print(int x) {
    if (x < 0)	putchar('-'), x = -x;
    if (x > 9)	print(x / 10);
    putchar(x % 10 + '0');
}
const int N = 1e3;
int Map[N + 10][N + 10], H[N + 10][N + 10];
pii pos[N * N + 10];
ll Cnt[N * N + 10];
bool Vis[N + 10][N + 10];
ll calc(int* h, int k) {
    stack<int>stack;
    vector<int>l(k), r(k);
    for (int i = k - 1; ~i; i--) {
        while (!stack.empty() && h[i] <= h[stack.top()])
            l[stack.top()] = i, stack.pop();
        stack.push(i);
    }
    while (!stack.empty())
        l[stack.top()] = -1, stack.pop();

    for (int i = 0; i < k; i++) {
        while (!stack.empty() && h[i] < h[stack.top()])
            r[stack.top()] = i, stack.pop();
        stack.push(i);
    }
    while (!stack.empty())
        r[stack.top()] = k, stack.pop();
    ll res = 0;
    for (int i = 0; i < k; i++)
        res += 1ll * h[i] * (i - l[i]) * (r[i] - i);
    return res;
}
int main() {
    int n = read(0), m = read(0);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            pos[Map[i][j] = read(0)] = { i,j };
    for (int d = 1; d <= n * m; d++) {
        vector<pii>temp;
        for (int t = d; t <= n * m; t += d) {
            auto [x, y] = pos[t];
            Vis[x][y] = true, temp.push_back({ x,y });
        }
        sort(temp.begin(), temp.end());
        for (int i = temp.size() - 1; ~i; i--) {
            auto [x, y] = temp[i];
            H[x][y] = Vis[x + 1][y] ? H[x + 1][y] + 1 : 1;
        }
        for (int i = 0, j; i < (int)temp.size(); i = j + 1) {
            for (j = i; j + 1 < (int)temp.size() && temp[j + 1].first == temp[i].first && temp[j + 1].second == temp[j].second + 1; j++);
            Cnt[d] += calc(H[temp[i].first] + temp[i].second, j - i + 1);
        }
        for (auto [x, y] : temp)  Vis[x][y] = false;
    }
    ll Ans = 0;
    for (int d = n * m; d; d--) {
        for (int t = d << 1; t <= n * m; t += d)
            Cnt[d] -= Cnt[t];
        Ans += 1ll * Cnt[d] * d;
    }
    printf("%lld\n", Ans);
    return 0;
}

G.Magic Spells

给定\(k\)个字符串\(S_1,S_2,...,S_k\),问有多少个本质不同的回文串?


建一棵回文树,然后在回文树上DFS

/*program from Wolfycz*/
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lMax 1e18
#define MK make_pair
#define iMax 0x7f7f7f7f
#define sqr(x) ((x)*(x))
#define pii pair<int,int>
#define UNUSED(x) (void)(x)
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
template<typename T>inline T read(T x) {
	int f = 1; char ch = getchar();
	for (; ch < '0' || ch>'9'; ch = getchar())	if (ch == '-')	f = -1;
	for (; ch >= '0' && ch <= '9'; ch = getchar())	x = (x << 1) + (x << 3) + ch - '0';
	return x * f;
}
inline void print(int x) {
	if (x < 0)	putchar('-'), x = -x;
	if (x > 9)	print(x / 10);
	putchar(x % 10 + '0');
}
const int N = 3e5;
struct PAM {
	int len[N + 10], fail[N + 10], ch[N + 10][26], tot, lst, num[N + 10];
	char s[N + 10];
	void init(char* ss) {
		tot = lst = 1;
		len[1] = -1, len[0] = 0, fail[0] = 1;
		for (int i = 1; ss[i]; ++i)s[i] = ss[i];
	}
	int insert(char cr, int ed) {
		int c = cr - 'a';
		int p = lst;
		while (s[ed] != s[ed - len[p] - 1])
			p = fail[p];
		if (!ch[p][c]) {
			int np = ++tot, q = fail[p];
			len[np] = len[p] + 2;
			while (s[ed] != s[ed - len[q] - 1])	q = fail[q];
			//for (; s[ed] != s[ed - len[q] - 1]; q = fail[q]);
			fail[np] = ch[q][c];
			num[np] = num[fail[np]] + 1;
			ch[p][c] = np;
		}
		lst = ch[p][c];
		return num[lst];
	}
}pams[5];
int Ans, k;
void dfs(int* pos) {
	Ans++;
	int temp[k];
	for (int i = 0; i < 26; ++i) {
		bool Flag = 1;
		for (int j = 0; j < k; j++)
			Flag &= (bool)pams[j].ch[pos[j]][i];
		if (!Flag)	continue;
		for (int j = 0; j < k; j++)
			temp[j] = pams[j].ch[pos[j]][i];
		dfs(temp);
	}
}

char s[N + 10];
int main() {
	//	freopen(".in","r",stdin);
	//	freopen(".out","w",stdout);
	k = read(0);
	for (int i = 0; i < k; i++) {
		memset(s, 0, sizeof(s));
		scanf("%s", s + 1);
		pams[i].init(s);
		for (int j = 1; s[j]; j++)
			pams[i].insert(s[j], j);
	}
	int pos[k];
	for (int i = 0; i < k; i++)	pos[i] = 0;
	dfs(pos);

	for (int i = 0; i < k; i++)	pos[i] = 1;
	dfs(pos);

	printf("%d\n", Ans - 2);
	return 0;
}

H.Radar Scanner

给定二维平面上\(n\)个点\((x,y)\)\(q\)次询问\((a,b)\)求所有满足\(ax+by>0\)的给定点的凸包面积


神仙题,咕咕咕

I.The Great Wall II

给定长度为\(n\)的序列,将其分为非空的\(k\)段使得每一段最大值之和最小,对\(k=1,2,...,n\)分别求解。\((n\leqslant 8\times 10^3)\)


\(F[i][j]\)表示将\(a_1,a_2,...,a_j\)分为\(i\)段的最小代价,显然有转移:\(F[i][j]=\min\limits_{1\leqslant k\leqslant j}\{F[i-1][k-1]+\max\{a_k,a_{k+1},...,a_j\}\}\),但这样转移是\(O(n^3)\)的,显然不可行

可以发现,当\(j\)固定时,\(k\)\(j\)遍历到1,\(\max\{a_k,a_{k+1},...,a_j\}\)是不减的,每一个取值对应一个\(k\)。新增一个\(a_{j+1}\)时末尾一些段会被合并成\(\max\)值为\(a_{j+1}\)的段,可以用单调栈维护。由于每个数只会入栈出栈一次,因此遍历一次的复杂度是\(O(n)\)

假设当前的段是\([l_1,r_1],[l_2,r_2],...,[l_s,r_s]\),对应的\(\max\)值是\(v_1,v_2,...,v_s\),我们可以把转移式改写为\(F[i][j]=\min\limits_{1\leqslant t\leqslant s}\{\min\{F[i-1][l_t],F[i-1][l_t+1],...,F[i-1][r-t]\}+v_t\}\),故我们只需要在\([l_t,r_t]\)对应的栈上记录\(mi_t=\min\{F[i-1][l_t],F[i-1][l_t+1],...,F[i-1][r_t]\}\),便可以快速转移。再维护整个单调栈里面的\(mi_t+v_t\)的前缀最小值即可。复杂度为\(O(n^2)\)

/*program from Wolfycz*/
#include<map>
#include<set>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lMax 1e18
#define MK make_pair
#define iMax 0x7f7f7f7f
#define sqr(x) ((x)*(x))
#define pii pair<int,int>
#define UNUSED(x) (void)(x)
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
template<typename T>inline T read(T x) {
    int f = 1; char ch = getchar();
    for (; ch < '0' || ch>'9'; ch = getchar())	if (ch == '-')	f = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar())	x = (x << 1) + (x << 3) + ch - '0';
    return x * f;
}
inline void print(int x) {
    if (x < 0)	putchar('-'), x = -x;
    if (x > 9)	print(x / 10);
    putchar(x % 10 + '0');
}
const int N = 8e3;
int F[N + 10][N + 10], A[N + 10], stack[N + 10];
int main() {
    int n = read(0);
    for (int i = 1; i <= n; i++)    A[i] = read(0);
    memset(F, 0x7f, sizeof(F));
    F[0][0] = 0;
    for (int j = 1; j <= n; j++) {
        int top = 0;
        for (int i = 1; i <= n; i++) {
            while (top && A[stack[top]] <= A[i]) {
                F[j][i] = min(F[j][i], F[j][stack[top]] - A[stack[top]] + A[i]);
                top--;
            }
            F[j][i] = min(F[j][i], F[j][stack[top]]);
            F[j][i] = min(F[j][i], F[j - 1][i - 1] + A[i]);
            stack[++top] = i;
        }
    }
    for (int i = 1; i <= n; i++)
        printf("%d\n", F[i][n]);
    return 0;
}

J.Colourful Journey

给一张\(n\)个点\(m\)条边的联通无向图,每条边上有一些颜色可以选用,\(q\)次询问从\(a\)出发走一条简单路到\(b\),初始颜色任意,每经过一条边就选边上一条颜色刷成当前颜色,问最大的颜色改变次数?


咕咕咕

K.NIO's OAuth2 Server

给定\(n\)\(\{1,2,...,k\}\)的非空子集,定义一个集合\(S\subseteq\{1,2,...,k\}\)的度数为使用最少个数的给定集合求并能得到\(S\)的超集,计算有多少个\(\{1,2,...,k\}\)的非空子集的度数分别是\(1,2,...,k\)


FWT,咕咕咕

posted @ 2022-09-08 00:43  Wolfycz  阅读(41)  评论(0编辑  收藏  举报