CodeForces1770 Good Bye 2022: 2023 is NEAR & LGR-127 MdOI Round 5

最后的赠礼。

CodeForces 1770

打得不怎么好,只做了 有一定思路但是还是靠 巨佬贺过去的。后面几道题都比较长没看。


A. Koxia and Whiteboards

题目简介

题目来源:

评测链接:https://codeforces.com/contest/1770/problem/A

形式化题意:给定一个长度为 的序列 ,有 个数 ,每次必须使用 代替任意一个 ,求 次操作之后最大化的 。多测。

数据范围:

考虑每一次用 去替代最小的 ,时间复杂度 。这是最朴素的模拟做法。也可以将题目转换为,从一个长度为 的序列中选择最大的 个,其中至少包含 ,这种做法的时间复杂度为 。也没快多少。


B. Koxia and Permutation

题目简介

题目来源:

评测链接:https://codeforces.com/contest/1770/problem/B

形式化题意:对于一个长度为 的序列 ,定义其 权值为一个长度为 的序列 的计算方式如下: ,而这个序列的权值 ,求对于一个 的全排列 ,构造一个序列使得 最小, ,多测。

数据范围:

有意思的构造题,考虑到 的计算关系到 中的极值,我们就要让其中一个极值在被计算的时候能把其它的极值给消耗掉,使其不会成为另外一些区间内被取到的极值。

我们猜测 ,那我们就让 两个数分别位于一个区间 的两端,当 的时候两端一定是极值,这样的话,位于其中的极值就会被 给消耗掉。但随着 向右推进的时候,也就是 不会被取到的地方,可能 就会再次被取到,我们就考虑将 提出来与其中和。

这样的话,我们就有一个构造的思路了,下面以 为例:

容易发现,任意相邻 个数的极值和都不会超过

维护双指针初始分别位于 ,以 为周期推进


C. Koxia and Number Theory

题目简介

题目来源:

评测链接:https://codeforces.com/contest/1770/problem/C

形式化题意:给定一个长度为 的序列 ,问是否存在正整数 使得 ,具体来讲,使得整个序列都加上一个非零整数 后两两互质,多测。

数据范围:

考虑 的性质,也是欧几里得 解法的第一步: ,那么我们就可以这样考虑: ,这样的话,可以使得 的一个数固定,会使得计算简洁很多。

可以考虑开一个桶 ,表示是否存在 的数存在。

那我们应该如何判断有无解,对于每一个 ,如果只存在一对数 满足 的话,则无解。也就是当只有 的时候无解。这样的话,无论 的取值为多少,都会使得有一组解会在 的时候不互质。

AC Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
read(Test);
while(Test--)
{
bool flag=0;
read(N);
int M=N<<1;
for(int i=1;i<=N;++i) read(a[i]);
std::sort(a+1,a+N+1);
for(int i=2;i<=N;++i)
if(a[i-1]==a[i]){ flag=1;break; }
if(flag){ puts("NO");continue; }
for(int i=1;i<=M;++i)
for(int j=0;j<=M;++j)
T[i][j]=0;
for(int i=1;i<=N;++i)
for(int j=1;j<=N;++j)
{
if(i==j) continue;
ll x=std::abs(a[i]-a[j]);
for(int k=2;k<=M;++k)
if(x%k==0) T[k][a[i]%k]=1;
}
for(int i=2;i<=M;++i)
{
ll sum=0;
for(int j=0;j<i;++j) sum+=T[i][j];
if(sum==i){ flag=1;break; }
}
if(flag) puts("NO");
else puts("YES");
}

后面都不会了,题也没读,有机会再做。


LGR - 127

全靠猜结论,喜提瞬时最高 rk3 。

A. Jump

题目简介

题目来源:

评测链接:https://www.luogu.com.cn/problem/P8918

形式化题意:给定一个 ,问是否存在一组解 使得 ,也就是将 拆分成 ,多测。

数据范围:

良心的出题人给了我们 组样例,我们可以大胆猜结论。

首先, 必然存在,所以当 为偶数时必然无解,样例也应证了我们的猜测说不定正确。然后,容易发现,当我们已经填到了第 位时, 的所有奇数都是可以到达的。因为我们填了第 位就相当于是把第 位左移了第 位,全部为 之后所能到达的最大值也是 ,而根据二进制填充,无论舍弃哪一位,都可以使得 的任意数都被到达(本题不可舍弃末位,所以偶数无解)。

那么,答案就呼之欲出了:

时间复杂度可以是 也可以是 嘛,主要是拼手速和算力。


B. Message

题目简介

题目来源:

评测链接:https://www.luogu.com.cn/problem/P8919

形式化题意:一共有 条信息,给定一个长度为 ,并指定 条特殊信息 ,每一次操作可以删除一条信息,问最少的操作次数使得

数据范围:

由于限定了 巨大的数据范围,所以估计是个线性的贪心思路。可以考虑,如果我们要使得第 条特殊信息不受惩罚,可以选择 之前的任意一条信息删除,但这个操作都可以用直接删除 代替,所以我们的贪心决策——只删除特殊信息。

有了这一个限制那就简单得多了,我们从前往后搜,记录一个 表示当前我们已经删除了多少条信息了,然后每次考虑 是否为 来考虑删不删除。

关键代码部分
1
2
3
4
5
6
7
8
read(N,M);
scanf("%s",f+1);
for(int i=1,x;i<=N;++i)
{
read(x);
if(f[x-delta]=='1') ++delta;
}
write(delta);

C. Variance

题目简介

题目来源:

评测链接:https://www.luogu.com.cn/problem/P8920

形式化题意:有两个长度为 的序列 ,要求构造一个序列 ,使得 的方差最大,求最大方差

数据范围:

特殊条件:

刚开始根本没啥思路,以为是一个什么神仙 ,然后就看见了那两个特殊条件,抽象一下,把 放到平面直角坐标系里,可以抽象成两条直线,但 始终在 之上。

然后偶然搜方差公式的时候(主要不知道那个 怎么转化,我太菜惹),看见一句话:

一个序列越分散差值越大,方差越大。

那么,显然, 本身都是单调递增序列,而 恒成立的事实告诉我们一个假设:我们把 切分成前后两部分,使得:

也就是既定一个 ,其前取 ,其后取 。信念告诉我们这种做法是对的。

然后说说这个 应该怎么优化。

首先列出方差的公式:

然后我们 之后化简:

我们记录一个 ,则:

这样就很好计算了,我们对于 分别维护一个前缀(后缀)和,枚举中结点 ,然后记录答案,时间复杂度 。需要开 __int128_t

AC Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// ----- Eternally question-----
// Problem: P8920 『MdOI R5』Variance
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P8920
// Memory Limit: 512 MB
// Time Limit: 1000 ms
// Written by: Eternity
// Time: 2022-12-31 19:17:28
// ----- Endless solution-------

#include<bits/stdc++.h>
#define re register
typedef long long ll;
template<class T>
inline void read(T &x)
{
x=0;
char ch=getchar(),t=0;
while(ch<'0'||ch>'9') t|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
if(t) x=-x;
}
template<class T,class ...T1>
inline void read(T &x,T1 &...x1){ read(x),read(x1...); }
template<class T>
inline void write(T x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
template<>
inline void write(bool x){ std::cout<<x; }
template<>
inline void write(char c){ putchar(c); }
template<>
inline void write(char *s){ while(*s!='\0') putchar(*s++); }
template<>
inline void write(const char *s){ while(*s!='\0') putchar(*s++); }
template<class T,class ...T1>
inline void write(T x,T1 ...x1){ write(x),write(x1...); }
template<class T>
inline bool checkMax(T &x,T y){ return x<y?x=y,1:0; }
template<class T>
inline bool checkMin(T &x,T y){ return x>y?x=y,1:0; }
using int128=__int128_t;
const int MAXN=1e6+10;
int N;
int128 a[MAXN],b[MAXN],c[MAXN];
int128 ans,Suma[MAXN],Sumb[MAXN];
int128 Suqa[MAXN],Suqb[MAXN];
inline int128 calc(int x)
{
int128 sum=Suma[x]+(Sumb[N]-Sumb[x]);
int128 suq=Suqa[x]+(Suqb[N]-Suqb[x]);
return N*suq-sum*sum;
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
read(N);
for(int i=1;i<=N;++i) read(a[i]),Suma[i]=Suma[i-1]+a[i],Suqa[i]=Suqa[i-1]+a[i]*a[i];
for(int i=1;i<=N;++i) read(b[i]),Sumb[i]=Sumb[i-1]+b[i],Suqb[i]=Suqb[i-1]+b[i]*b[i];
ans=N*Suqb[N]-(Sumb[N]*Sumb[N]);
for(int i=1;i<=N;++i) checkMax(ans,calc(i));
write(ans);
return 0;
}
/*

*/

D. Triangulation

题目简介

题目来源:

评测链接:https://www.luogu.com.cn/problem/P8921

形式化题目:给定一个正 多边形的三角剖分,转化为一个有 个结点, 条边的无向图。求一种构造这张图的生成树使得每一个结点的度数为奇数。

数据范围:

考场上并没有想到,两场都被构造卡掉惹。可惜没有

根据样例以及 以及排行榜可知——当 为奇数时无解。

这个的证明其实也是很简单的,对于一张图的生成树一共会有 条边,所以度数和应该为 ,是偶数。但如果要满足要求的话,每一个结点度数应该是奇数,一共有 个结点,也是奇数,计算得出的度数和应当为奇数,与生成树性质相悖,所以无解。

然后考虑如何构造。

由于给定的图是一个三角剖分,那我们也可以从三角形的方面来考虑生成树。如果我们当前已经拥有了一个内部封闭的 边形,然后在这个图形的外围再添上一个三角形,对已知结点的度数会做出什么改变。

会新增一个度数为 的结点,并改变其相连的那 个结点的奇偶性,这是显然的。我们保证了原先的图是一个满足要求的生成树结构,所以我们只需要考虑现在被改变了的三个结点。

我们试图简化这个问题,求问是否存在一种构造方案使得每个结点的度数都不超过 ,这样的话,我们可以构造出的生成树是一棵二叉树。

那我们按以下操作来整:

  • 如果结点 的两个子结点都有偶数个三角形,则 与其父结点相连;
  • 如果结点 的两个子结点都有奇数个三角形,则 充当外添三角形的外添结点。
  • 如果结点 的两个子结点一奇一偶,则让 与奇子结点充当外添三角形的内接结点。

按照这样的话,至少在执行这一步的是正确的。对于实现,我们可以分为两步走。

第一步构造出对偶图,给出 个长度和为 的数列全排列排序,用计数排序实现,时间复杂度 ,求出每一个结点的前驱后继。可以放 个哈希表记录每一个结点在 std::vector 中的下标。

第二步区分三角形的边,按照一定顺序排列。

AC Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// ----- Eternally question-----
// Problem: P8921 『MdOI R5』Triangulation
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P8921
// Memory Limit: 512 MB
// Time Limit: 1000 ms
// Written by: Eternity
// Time: 2023-01-03 11:25:51
// ----- Endless solution-------

#include<bits/stdc++.h>
#define re register
typedef long long ll;
template<class T>
inline void read(T &x)
{
x=0;
char ch=getchar(),t=0;
while(ch<'0'||ch>'9') t|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
if(t) x=-x;
}
template<class T,class ...T1>
inline void read(T &x,T1 &...x1){ read(x),read(x1...); }
template<class T>
inline void write(T x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
template<>
inline void write(bool x){ putchar(x?'1':'0'); }
template<>
inline void write(char c){ putchar(c); }
template<>
inline void write(char *s){ while(*s!='\0') putchar(*s++); }
template<>
inline void write(const char *s){ while(*s!='\0') putchar(*s++); }
template<class T,class ...T1>
inline void write(T x,T1 ...x1){ write(x),write(x1...); }
template<class T>
inline bool checkMax(T &x,T y){ return x<y?x=y,1:0; }
template<class T>
inline bool checkMin(T &x,T y){ return x>y?x=y,1:0; }
const int MAXN=3e5+10,MAXM=1e6+10;
int N,M;
struct G
{
int next,to;
}Edge[MAXM<<1];
int Head[MAXN],Total;
inline void addEdge(int u,int v)
{
Edge[++Total]=(G){Head[u],v};Head[u]=Total;
Edge[++Total]=(G){Head[v],u};Head[v]=Total;
}
std::vector<int>G[MAXN];
std::unordered_map<int,int>Loc[MAXN];
#define lc G[u][Loc[w][u]+1]
#define rc G[v][Loc[w][v]-1]
int dfsTree(int u,int v,int w)
{
int lz=0,rz=0;
if(!(w==u-1||w==N&&u==1)) lz=dfsTree(u,w,lc);
if(!(w==v+1||w==1&&v==N)) rz=dfsTree(w,v,rc);
if((lz+rz)&1)
{
if(lz&1==1) write(u,' ',w,'\n',u,' ',lc,'\n');
else write(v,' ',w,'\n',v,' ',rc,'\n');
}
else if(lz&1==1) write(w,' ',lc,'\n',w,' ',rc,'\n');
return lz+rz+1;
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
read(N);addEdge(1,N);
for(int u=2;u<=N;++u) addEdge(u-1,u);
for(int i=4,u,v;i<=N;++i){ read(u,v);addEdge(u,v); }
if(N&1) return puts("-1"),0;
for(int x=1;x<=N;++x)
for(int e=Head[x],v;e;e=Edge[e].next)
{
v=Edge[e].to;
if(v>x) continue;
Loc[x][v]=G[v].size();
G[v].push_back(x);
}
for(int x=1;x<=N;++x)
for(int e=Head[x],v;e;e=Edge[e].next)
{
v=Edge[e].to;
if(v<x) continue;
Loc[x][v]=G[v].size();
G[v].push_back(x);
}
write("1 2\n");
dfsTree(1,2,G[1][1]);
return 0;
}
/*

*/

后面的 事神仙 和多项式,所以不做。