"蔚来杯"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,咕咕咕