2022.10.17 模拟赛小结
2022.10.17 模拟赛小结
一场 ACM 模拟赛
题面PDF链接
(这个链接只是为了自己方便找,页面设置权限了,不要尝试访问)
更好的阅读体验戳此进入
(建议您从上方链接进入我的个人网站查看此 Blog,在 Luogu 中图片会被墙掉,部分 Markdown 也会失效)
赛时思路
T1
对于 $ n $ 个正整数的序列 $ a_n $,给定 $ k $,每次操作选择大于 $ k $ 的 $ a_i $,使 $ a_i \leftarrow a_i - 1 $,然后会使 $ a_{i - 1} \leftarrow a_{i - 1} + 1 $ 或 $ a_{i + 1} \leftarrow a_{i + 1} + 1 $。求任意次操作后最大能选出多长的子串满足每个数都不小于 $ k \(,\) T $ 组询问。
原题 LG-P3503 [POI2010]KLO-Blocks。
赛时没想出来,最后糊了一个乱搞上去,然后乱搞也寄了,直接 $ 0\texttt{pts} $。
一直在考虑维护把大于 $ k $ 的数推平然后补齐小于 $ k $ 的数,然后口糊证明了一下对于每一个大于 $ k $ 的数优先向右或向左一定不劣于左右各自推平一部分,不过这玩意依然是 $ O(2^n) $ 的,然后设计了一个类似并查集的东西来维护向左向右的下一个未满足要求的数,然后常数也巨大。。
Code
#define _USE_MATH_DEFINES
#include <bits/stdc++.h>
#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW(arr) void* Edge::operator new(size_t){static Edge* P = arr; return P++;}
/******************************
abbr
******************************/
using namespace std;
mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}
typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;
#define TIME_LIMIT ((double)clock() / CLOCKS_PER_SEC <= 0.9 / (double)M)
template<typename T = int>
inline T read(void);
int N, M;
int cnt;
int k;
int base[1100000], a[1100000];
int ans(0);
bool finished(false);
vector < int > high;
struct UnionFind{
int fal[1100000], far[1100000];
int FindL(int x){return fal[x] == x ? x : fal[x] = FindL(fal[x]);}
int FindR(int x){return far[x] == x ? x : far[x] = FindR(far[x]);}
void UnionL(int f, int s){fal[s] = FindL(f);}
void UnionR(int f, int s){far[s] = FindR(f);}
void reset(int N, int k){
memset(fal, 0, sizeof(fal));
memset(far, 0, sizeof(far));
for(int i = 1; i <= N; ++i)
if(a[i] < k)fal[i] = i;
else if(a[i] > k)high.push_back(i), fal[i] = FindL(i - 1);
else fal[i] = FindL(i - 1);
for(int i = N; i >= 1; --i){
if(a[i] < k)far[i] = i;
else far[i] = FindR(i + 1);
}
}
}uf;
void dfs(int dep = 1){
// printf("in dfs, dep = %d\n", dep);
if(finished)return;
if(!TIME_LIMIT)return finished = true, void();
if(dep > (int)high.size()){
int len(0);
for(int i = 1; i <= N; ++i){
if(a[i] >= k)++len;
else len = 0;
}
ans = max(ans, len);
return;
}
int p = high.at(dep - 1);
vector < pair < int, int > > editA, editL, editR;
if(rnddd(50)){
int val = p - k;
int cp = uf.FindR(p);
while(cp && val > 0){
editA.emplace_back(cp, a[cp]);
editR.emplace_back(cp, uf.FindR(cp));
val -= k - a[cp];
a[cp] = k;
int tcp = cp;
if(val < 0)a[cp] += val;
else cp = uf.FindR(cp + 1), uf.UnionR(cp, tcp);
}
cp = uf.FindL(p);
while(cp && val > 0){
editA.emplace_back(cp, a[cp]);
editL.emplace_back(cp, uf.FindL(cp));
val -= k - a[cp];
a[cp] = k;
int tcp = cp;
if(val < 0)a[cp] += val;
else cp = uf.FindL(cp - 1), uf.UnionL(cp, tcp);
}
if(val > 0){
ans = N;
finished = true;
return;
}
dfs(dep + 1);
for(auto i : editA)a[i.first] = i.second;
for(auto i : editL)uf.fal[i.first] = i.second;
for(auto i : editR)uf.far[i.first] = i.second;
}else{
int val = p - k;
int cp = uf.FindL(p);
while(cp && val > 0){
editA.emplace_back(cp, a[cp]);
editL.emplace_back(cp, uf.FindL(cp));
val -= k - a[cp];
a[cp] = k;
int tcp = cp;
if(val < 0)a[cp] += val;
else cp = uf.FindL(cp - 1), uf.UnionL(cp, tcp);
}
cp = uf.FindR(p);
while(cp && val > 0){
editA.emplace_back(cp, a[cp]);
editR.emplace_back(cp, uf.FindR(cp));
val -= k - a[cp];
a[cp] = k;
int tcp = cp;
if(val < 0)a[cp] += val;
else cp = uf.FindR(cp + 1), uf.UnionR(cp, tcp);
}
if(val > 0){
ans = N;
finished = true;
return;
}
dfs(dep + 1);
for(auto i : editA)a[i.first] = i.second;
for(auto i : editL)uf.fal[i.first] = i.second;
for(auto i : editR)uf.far[i.first] = i.second;
}
}
int main(){
freopen("TOMO.in", "r", stdin);
freopen("TOMO.out", "w", stdout);
N = read(), M = read();
for(int i = 1; i <= N; ++i)base[i] = read();
while(M--){
memcpy(a, base, sizeof(base));
cnt = ans = finished = 0;
high.clear();
k = read();
uf.reset(N, k);
dfs();
printf("%d\n", ans);
}
fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
return 0;
}
template<typename T>
inline T read(void){
T ret(0);
short flag(1);
char c = getchar();
while(c != '-' && !isdigit(c))c = getchar();
if(c == '-')flag = -1, c = getchar();
while(isdigit(c)){
ret *= 10;
ret += int(c - '0');
c = getchar();
}
ret *= flag;
return ret;
}
T2
写了个 DP,假了,爆零,寄。
T3
原题 CF840E In a Trap。
大概就是写了个暴力,然后数据比较奇怪,还多过了几个点。。最后本场唯一的 $ 60\texttt{pts} $。
Code
#define _USE_MATH_DEFINES
#include <bits/stdc++.h>
#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW(arr) void* Edge::operator new(size_t){static Edge* P = arr; return P++;}
/******************************
abbr
******************************/
using namespace std;
mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}
typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;
template<typename T = int>
inline T read(void);
struct Edge{
Edge* nxt;
int to;
OPNEW;
}ed[310000];
ROPNEW(ed);
Edge* head[150000];
int N, Q;
int val[150000];
int dep[150000];
int pre[150000];
void dfs(int p = 1, int fa = 0){
dep[p] = dep[fa] + 1;
pre[p] = fa;
for(auto i = head[p]; i; i = i->nxt)
if(SON != fa)dfs(SON, p);
}
int Make(int S, int T){
int ret(-1);
if(dep[S] >= dep[T]){
int SS = S;
while(T != SS){
ret = max(ret, val[SS] ^ abs(dep[SS] - dep[S]));
// printf("val: %d xor %d = %d\n", val[S], abs(dep[T] - dep[S]), val[S] ^ abs(dep[T] - dep[S]));
SS = pre[SS];
}ret = max(ret, val[SS] ^ abs(dep[SS] - dep[S]));
}else{
while(S != T){
ret = max(ret, val[S] ^ abs(dep[T] - dep[S]));
// printf("val: %d xor %d = %d\n", val[S], abs(dep[T] - dep[S]), val[S] ^ abs(dep[T] - dep[S]));
T = pre[T];
}ret = max(ret, val[S] ^ abs(dep[T] - dep[S]));
}
// int len = abs(dep[S] - dep[T]);
// printf("len s = %d, t = %d, is %d\n", S, T, len);
// if(dep[s] < dep[t])swap(s, t);
// while(S != T){
// ret = max(ret, val[S] ^ abs(dep[T] - dep[S]));
// printf("val: %d xor %d = %d\n", val[S], abs(dep[T] - dep[S]), val[S] ^ abs(dep[T] - dep[S]));
// S = pre[S];
// }ret = max(ret, val[S] ^ 0);
return ret;
}
int main(){
freopen("CP0A.in", "r", stdin);
freopen("CP0A.out", "w", stdout);
N = read(), Q = read();
for(int i = 1; i <= N; ++i)val[i] = read();
for(int i = 1; i <= N - 1; ++i){
int s = read(), t = read();
head[s] = new Edge{head[s], t};
head[t] = new Edge{head[t], s};
}dfs();
while(Q--){
int s = read(), t = read();
printf("%d\n", Make(t, s));
}
fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
return 0;
}
template<typename T>
inline T read(void){
T ret(0);
short flag(1);
char c = getchar();
while(c != '-' && !isdigit(c))c = getchar();
if(c == '-')flag = -1, c = getchar();
while(isdigit(c)){
ret *= 10;
ret += int(c - '0');
c = getchar();
}
ret *= flag;
return ret;
}
T4
原题 LG-P8386 [PA 2021] Od deski do deski。
不会,寄。
正解
T1
首先问题显然可以转化为,找一段最长的区间,满足区间的平均数大于等于 $ k $。然后再转化为把每个数减去 $ k $,然后要找最长的一段区间,满足区间加和大于 $ 0 $。再转化为前缀和后就是要找最长的一段 $ s_j - s_i \ge 0 $,显然 $ O(n^2) $ 枚举会寄,考虑单调栈,对于左端点,显然若有 $ i \lt j \land s_i \le s_j $ 那么 $ i $ 一定优于 $ j $,或者说一定不会选择到 $ j $,证明显然。于是单调栈维护左端点,单调递减,右端点单调递增,这样可以对于每个左端点二分右端点,反之亦然,复杂度 $ O(Q n \log n) $ 不正确。考虑维护左端点的单调栈后,对于右端点从 $ n $ 向 $ 1 $ 枚举,若 $ s_i \ge 0 $ 那么 $ [0, i] $ 一定合法,否则考虑如果当前点符合右端点单调栈的规则,那么开始和左端点的单调栈相匹配,然后左端点出栈,并更新答案,显然这里出栈的左端点一定在后面不会再用到了。
Code
#define _USE_MATH_DEFINES
#include <bits/extc++.h>
#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW(arr) void* Edge::operator new(size_t){static Edge* P = arr; return P++;}
/******************************
abbr
******************************/
using namespace std;
using namespace __gnu_pbds;
mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}
typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;
#define int ll
template<typename T = int>
inline T read(void);
int N, Q;
int K;
int s[1100000];
stack < int > lft;
int ans(0);
void Solve(void){
ans = 0;
for(int i = 1; i <= N; ++i){
s[i] -= K * i;
if(lft.empty() || s[i] < s[lft.top()])lft.push(i);
}
// for(int i = 1; i <= N; ++i)printf("%d%c", s[i], i == N ? '\n' : ' ');
int tpr(INT_MIN);
for(int i = N; i >= 1; --i){
if(s[i] >= 0)ans = max(ans, i);
if(i == N || s[i] > tpr){
while(!lft.empty() && s[i] - s[lft.top()] >= 0)
ans = max(ans, i - lft.top()), lft.pop();
tpr = s[i];
}
}
for(int i = 1; i <= N; ++i)s[i] += K * i;
while(!lft.empty())lft.pop();
}
signed main(){
N = read(), Q = read();
for(int i = 1; i <= N; ++i)s[i] = read() + s[i - 1];
while(Q--){
K = read();
Solve();
printf("%lld%c", ans, Q ? ' ' : '\n');
}
fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
return 0;
}
template<typename T>
inline T read(void){
T ret(0);
short flag(1);
char c = getchar();
while(c != '-' && !isdigit(c))c = getchar();
if(c == '-')flag = -1, c = getchar();
while(isdigit(c)){
ret *= 10;
ret += int(c - '0');
c = getchar();
}
ret *= flag;
return ret;
}
T2
咕咕咕。
T3
咕咕咕。
T4
咕咕咕。
UPD
update-2022_10_17 初稿

浙公网安备 33010602011771号