整理的算法模板合集: ACM模板

点我看算法全家桶系列!!!

实际上是一个全新的精炼模板整合计划


目录

  • 【每日亿题】Codeforces Round #698 (Div. 2)(A ~ F)6题全,超高质量题解)2021/2/4
  • A - Nezzar and Colorful Balls
  • B - Nezzar and Lucky Number
  • C - Nezzar and Symmetric Array
  • D - Nezzar and Board
  • E - Nezzar and Binary String
  • F - Nezzar and Nice Beatmap(每日妙妙题精选)

【每日亿题】Codeforces Round #698 (Div. 2)(A ~ F)6题全,超高质量题解)2021/2/4


比赛链接:https://codeforces.com/contest/1478

A - Nezzar and Colorful Balls

Problem A - Nezzar and Colorful Balls

给你 nnn 个球 1⋯n1\cdots n1⋯n,每个球上有一个数字,所有球上的数字组成的序列经排序以后是一个不下降的数列。我们要为每一个球都染上颜色,保证我们使用的每一种颜色,染上这个颜色的球上的数字组成的序列经排序后一定是严格上升的数列。问我们最少需要多少种颜色才能在符合要求的前提下将所有的球都染上色。

1≤T,n≤1001\le T,n\le1001≤T,n≤100

Solution

签到题,只要读懂题意模拟一下样例就行了,我们发现答案就是相同数字的最大个数。因为每个颜色能染的球,球上的数字必须是严格升序而不能是相等,所以也就意味着对于相同数字的球,我们每一种颜色都只能染一个球,最少需要的颜色数量就是相同数字的球的最大数量。我们直接用桶记录一下 ~ 算一下这个最大个数即可。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>using namespace std;const int N = 50007;int n, m, t;
int a[N];
int ans;
int vis[N];int main()
{scanf("%d", &t);while(t -- ) {ans = 0;memset(vis, 0, sizeof vis);scanf("%d", &n);for(int i = 1; i <= n; ++ i) {scanf("%d", &a[i]);vis[a[i]] ++ ;}for(int i = 1; i <= n; ++ i) {ans = max(ans, vis[a[i]]);}printf("%d\n", ans);}return 0;
}

B - Nezzar and Lucky Number

Problem B Nezzar and Lucky Number

给定 qqq 个正整数,问能不能拆分一个或多个十进制表示法上有 ddd 的正整数。 ( 1≤q≤104,1≤d≤91 \le q \le 10^4,1 \le d \le 91≤q≤104,1≤d≤9 1≤ai≤1091 \le a_i \le 10^91≤ai​≤109.

Solution

因为数据比较大,这种题目一般数达到一个限制以后,答案就不会变了。

我们发现一旦 x≥10∗dx\ge 10*dx≥10∗d,就一定能被 “幸运数字” 拼出来。

因为 [10∗d,10∗d+9][10*d,10*d+9][10∗d,10∗d+9] 都是幸运数字,例如 d=7d=7d=7 [70,79][70,79][70,79] 均为幸运数字(均含有 d=7d=7d=7),这样我们在 [10∗d,10∗d+9][10*d,10*d+9][10∗d,10∗d+9] 的基础之上,都加上 ddd ,得到 [10∗d+d,10∗d+9+d]→[11∗d,11∗d+9],(1≤d≤9)[10*d+d,10*d+9+d] \to [11*d,11*d+9],(1\le d \le 9)[10∗d+d,10∗d+9+d]→[11∗d,11∗d+9],(1≤d≤9) 均为 YES。而 10∗d+9+d10*d+9+d10∗d+9+d 的下一位,也就是10∗d+9+d+1=12∗d10*d+9+d+1=12*d10∗d+9+d+1=12∗d,同样是幸运数字,这样我们再在[11∗d,11∗d+9][11*d,11*d+9][11∗d,11∗d+9]的基础之上,同样加上 ddd ,以此类推,我们发现只要 x≥10∗dx\ge 10*dx≥10∗d ,答案均为 YES

然后我们只需要考虑一下 x≤10∗dx\le10*dx≤10∗d 的情况即可。因为 1≤d≤91\le d\le91≤d≤9,所以这些数不会很多,我们只需要预处理一下即可。可以直接爆搜,将所有的YES 数字加起来,标记和也为 YES 或者动规,甚至手动打表,因为最多只有几百个数。

  • 动规预处理版本的AC代码
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <vector>using namespace std;
const int N = 1e4 + 7, mod = 1e9 + 7;
typedef long long ll;
int n, m, t, x;map<int, int> ans[N];
int a[N];
bool vis[N];void solve(int d)
{ans[d][0] = 1;int limit = 10 * d;for(int i = 0; 10 * i + d <= limit; ++ i) {for(int j = 0; 10 * i + d + j <= limit; ++ j) {ans[d][10 * i + d + j] |= ans[d][j];}}ans[d][0] = 0;
}void init()
{for(int i = 1; i <= 9; ++ i) {solve(i);}
}int d;int main()
{scanf("%d", &t);init();while(t -- ) {scanf("%d%d", &n, &d);for(int i = 1; i <= n; ++ i) {int x;scanf("%d", &x);if(x >= 10 * d) {puts("YES");}else {if(ans[d][x]) {puts("YES");}else {puts("NO");}}}}return 0;
}

写题解看别人题解的时候发现的一个思路:

我们发现一个结论:

若这是 aia_iai​ 满足: ai−k⋅da_i - k \cdot dai​−k⋅d

其中 k∈[1,9]k \in [1,9]k∈[1,9] 的话,它就可以,反则反之。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <vector>using namespace std;
const int N = 1e4 + 7, mod = 1e9 + 7;
typedef long long ll;
typedef int itn;itn n, m, q, d;
itn t;
int a[N];bool solve(itn x)
{if(x >= 10 * d)return true;for(;x >= d; x -= d)if(x % 10 == d)return true;return false;
}int main()
{scanf("%d", &t);while(t -- ) {scanf("%d%d", &q, &d);for(int i = 1; i <= q; ++ i) {itn x;scanf("%d", &x);if(solve(x))puts("YES");else puts("NO");}}return 0;
}

C - Nezzar and Symmetric Array

定义长度为 2×n2 \times n2×n 的数组 aaa ,满足:

aaa 中的元素不重复。

并且对于任意一个下标i(1≤i≤2⋅n,i≠j)i(1 \leq i \leq 2 \cdot n, i \ne j)i(1≤i≤2⋅n,i​=j),都能找到一个下标 jjj,使得 ai=−aja_i = -a_jai​=−aj​ 。

现在给定一个数组 ddd,其中

di=∑j=12n∣ai−aj∣d_i = \sum_{j=1}^{2n}|a_i -a_j|di​=j=1∑2n​∣ai​−aj​∣
问能不能通过数组 ddd 构造出数组 aaa 。

能构造出输出 YES,不能就输出 NO
有 TTT 组数据,其中 1≤T≤105,1≤n≤105,1≤di≤10121\le T \le10^5,1\le n \le10^5,1\le d_i\le10^{12}1≤T≤105,1≤n≤105,1≤di​≤1012

并且保证 (∑n∈onetextcasen)≤105(\sum_{n\in\ \text one\ \text text\ \text case} n ) \le\ 10^5(∑n∈ one text case​n)≤ 105

Solution

一般这种有 nnn 个的数组,nnn 会很大,求一种 value\tt valuevalue 什么的,一定是一种规律性的问题。

所以我们一般从两个数开始,从特殊到一般,一般简单推一下就能找到拓展到 nnn 个数的答案。

我们首先分析两个的情况。

我们假设两个正整数 a,ba,ba,b

那么序列就会有四个数:a,b,−a,−ba,b,-a,-ba,b,−a,−b

d1=∣a−a∣+∣a−b∣+∣a−(−a)∣+∣a−(−b)∣d_1=|a-a|+|a-b|+|a-(-a)|+|a-(-b)|d1​=∣a−a∣+∣a−b∣+∣a−(−a)∣+∣a−(−b)∣
=∣a−b∣+∣a+b∣+∣2a∣\ \ \ =|a-b|+|a+b|+|2a|   =∣a−b∣+∣a+b∣+∣2a∣

d2=∣b−b∣+∣b−a∣+∣b−(−a)∣+∣b−(−b)∣d_2=|b-b|+|b-a|+|b-(-a)|+|b-(-b)|d2​=∣b−b∣+∣b−a∣+∣b−(−a)∣+∣b−(−b)∣
=∣b−a∣+∣b+a∣+∣2b∣\ \ \ \ \ =|b-a|+|b+a|+|2b|     =∣b−a∣+∣b+a∣+∣2b∣
当 a>ba>ba>b 时:

∣a−b∣=a−b|a-b|=a-b∣a−b∣=a−b
∣a+b∣=a+b|a+b|=a+b∣a+b∣=a+b

d1=2a+2a=4ad_1=2a+2a=4ad1​=2a+2a=4a

d2=a−b+a+b+2b=2a+2bd_2=a-b+a+b+2b=2a+2bd2​=a−b+a+b+2b=2a+2b

当 a<ba<ba<b 时

∣a−b∣=b−a|a-b|=b-a∣a−b∣=b−a
∣a+b∣=a+b|a+b|=a+b∣a+b∣=a+b

d1=2a+2bd_1=2a+2bd1​=2a+2b
d2=4bd_2=4bd2​=4b

我们发现

若 a>ba>ba>b 则 d1>d2d_1>d_2d1​>d2​

若 a<ba<ba<b 则 d1<d2d_1<d_2d1​<d2​

我们发现题中给我们的数列 ddd 和实际上想要我们求的数列 aaa 是同步的,同单调性的,所以很明显我们可以将数列从大到排序

然后拓展到 nnn 个数字:

d1=2×na1=2na1d_1=2\times na_1=2na_1d1​=2×na1​=2na1​

d2=2a1+2×(n−(2−1))a2d_2=2a_1+2\times(n-(2-1))a_2d2​=2a1​+2×(n−(2−1))a2​

d3=2a1+2a2+(2×(n−(3−1)))a3d_3=2a_1+2a_2+(2\times(n-(3-1)))a_3d3​=2a1​+2a2​+(2×(n−(3−1)))a3​

⋯\cdots⋯

di=2a1+2a2+⋯+2ai−1+2×(n−(i−1))aid_i=2a_1+2a_2+\cdots+2a_{i-1}+2\times(n-(i-1))a_idi​=2a1​+2a2​+⋯+2ai−1​+2×(n−(i−1))ai​

(n−(i−1))ai(n-(i-1))a_i(n−(i−1))ai​ 表明前面有 i−1i-1i−1 个比他大的数。

因为排过序了,所以想要构造出答案,就从前往后,依次按照公式,减掉前面刚刚求得的 a1a_1a1​ ~ ai−1a_{i-1}ai−1​ 然后除以系数就是当前的 a−ia-ia−i。

推出公式之后,我们可以手算样例,发现不存在解的情况:

  • did_idi​ 不能整除 2×(n−(i−1))2\times(n-(i-1))2×(n−(i−1)),因为 aia_iai​ 全是整数,所以很明显。

  • 手算倒数第二组数据的时候发现, did_idi​ 不能小于 000 ,因为题目中要求的是正整数。

  • 以及因为我们证明了 aia_iai​ 一定和 did_idi​ 是同单调性的,所以如果我们发现上一个 ai−1a_{i-1}ai−1​ 是小于 aia_iai​ ,则同样无解。

所以整体的思路就是先输入,去重得到 nnn 个数,然后根据上面得到的三个结论判断是否存在解。

然后就是实现上的细节了,因为 di≤1012d_i\le 10^{12}di​≤1012,所以我们去重的时候可以用 unordered_map 来当作 hash 表使用,普通数组存不下。


注意本题究极卡常

本题的最优解时间复杂度为:O(Tnlogn)O(Tnlogn)O(Tnlogn)

而 T,n≤105T,n\le10^5T,n≤105,还好保证了 ∑textcasen≤105\sum_{\text text\ \text case} n\le10^5∑text case​n≤105,所以我们上面看上去很离谱的时间复杂度可以勉强卡过…

所以我们要减少任何会浪费时间的写法
比如:

  • memset 改为 for 循环初始化,注意 n≤105n\le10^5n≤105,而输入的数据有 2∗n2*n2∗n ,虽然我们不需要存这么多,但是如果我们使用 for 循环初始化而不是用 memset ,那我们的数组就要开二倍以上

  • 不要偷懒用 reverse !能直接写 cmp ,写个 reverse 还多了个常数…

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <vector>
#include <unordered_map>using namespace std;
typedef long long ll;
typedef int itn;
const int N = 5e5 + 7, mod = 1e9 + 7;
const ll INF = 1e18 + 7;itn n, m, t;
ll d[N];
unordered_map<ll, int> vis;bool cmp(ll a, ll b)
{return a > b;
}int main()
{scanf("%d", &t);while(t -- ) {int cnt = 0;bool flag = 1;scanf("%d", &n);for(int i = 1; i <= 2 * n; ++ i)d[i] = 0;vis.clear();for(int i = 1; i <= 2 * n; ++ i) {ll x;scanf("%lld", &x);if(vis[x] == 0)d[ ++ cnt] = x, vis[x] = 1;else vis[x] ++ ;}sort (d + 1, d + 1 + cnt, cmp);//reverse (d + 1, d + 1 + cnt);//就多用了一个 reverse 结果T了...//这题时间卡的非常的紧, T, n ≤ 10^5...itn deer = 2 * n;ll sum = 0, last = INF;for(int i = 1; i <= cnt; ++ i) {if(vis[d[i]] != 2) {flag = false;break;}d[i] -= sum;int mul = 2 * (n - (i - 1));if(d[i] % mul != 0 || d[i] <= 0) {flag = false;break;}//cout << d[i] << "mul" << mul << "?" << endl;ll a = d[i] / mul;if(last <= a) {flag = false;break;}last = a;//cout << a << endl;sum += 2 * a;}if(flag) {puts("YES");}else {puts("NO");}}return 0;
}

D - Nezzar and Board

Problem A Nezzar and Board

我们在黑板上写了 nnn 个数,x1,x2,⋯,xnx_1,x_2,\cdots,x_nx1​,x2​,⋯,xn​。

我们可以无限此地使用一个操作:从黑板上的数字中选择两个数 xxx 和 yyy (xxx 和 yyy 可以是同一个数)将 2x−y2x-y2x−y 写到黑板上去(xxx 和 yyy 还在)。求整数 KKK 能否被写到黑板上去。

1≤T≤105,1≤k,xi≤10181\le T\le10^5,1\le k,x_i\le10^{18}1≤T≤105,1≤k,xi​≤1018.

Solution

我这里不需要猜结论或者证明结论,我的解法只需要一步步按部就班地往后推就行了

首先我们知道每次可以选择两个数 xxx 和 yyy 得到 2x−y2x-y2x−y。

这个 2x−y2x-y2x−y 看上去毫无头绪,因为一个乘以二,一个是本身,两个好像和 xxx 与 yyy 毫无关系。我们可以把它们拆开,得到 x+(x−y)x+(x-y)x+(x−y)。

逐渐好起来了,看上去有些头绪了,因为这里实际上表示的是从一个数 xxx 开始,加上这个数与另一个数的差值,然后得到了一个新数。

我们可以模拟一下:

我们假设:

xi=xj+(xj−xk)x_i=x_j+(x_j-x_k)xi​=xj​+(xj​−xk​)

xa=xi+(xi−xc)x_a=x_i+(x_i-x_c)xa​=xi​+(xi​−xc​)

xb=xa+(xa−xd)x_b=x_a+(x_a-x_d)xb​=xa​+(xa​−xd​)

那么 xbx_bxb​ 就可以表示为:

xb=xj+(xj−xk)+(xi−xc)+(xa−xd)x_b=x_j+(x_j-x_k)+(x_i-x_c)+(x_a-x_d)xb​=xj​+(xj​−xk​)+(xi​−xc​)+(xa​−xd​)

我们可以从特殊到一般,得到所有能写到黑板上的数的表达式(为了理解方便,看起来舒服,我们就设能写到黑板上的数为我们题目中想要我们来判断的 KKK):

那么我们得到了一个表达式:

xi+∑j,k(xj−xk)=Kx_i+\sum_{j,k}(x_j-x_k)=Kxi​+∑j,k​(xj​−xk​)=K

其中 j,kj,kj,k 可以是我们得到 KKK 的途中使用的任意一个数的下标,甚至 xix_ixi​ 可以替换为任意一个 xxx ,这一点我们下面再详细说明。

特别的,由于题目中特别说明了可以每次选的两个数 xxx 和 yyy 可以是同一个数(" not necessarily distinct ")所以我们这个表达式不仅可以表示新写到黑板上的数,还可以表示原来就在黑板上的数:x=x+(x−x)x=x+(x-x)x=x+(x−x)。

这样我们得到的这个表达式就可以表示所有的黑板上的数字了。

我们可以把左边的 xix_ixi​ 移到右边,这样两边的形式看上去统一一些,并且也不会影响答案的正确性。

即:

∑j,k(xj−xk)=K−xi\sum_{j,k}(x_j-x_k)=K-x_ij,k∑​(xj​−xk​)=K−xi​

其中, xix_ixi​ 可以换为 x1x_1x1​ ,因为不管 xix_ixi​ 是谁,我们都可以移动到左边,变成正数 xix_ixi​,而黑板上的任意一个数都可以由 x1x_1x1​ 通过两次操作得到,例如我们想由 x1x_1x1​ 得到 xnx_nxn​ :

第一次操作: 选择 x1x_1x1​ 和 xnx_nxn​ ,得到新数:x1+(x1−xn)x_1+(x_1-x_n)x1​+(x1​−xn​),写到黑板上。
第二次操作: 选择 x1x_1x1​ 和 x1+(x1−xn)x_1+(x_1-x_n)x1​+(x1​−xn​) ,得到:x1+{x1−[x1+(x1−xn)]}=x1+x1−x1−x1+xn=xnx_1+\{x_1-[x_1+(x_1-x_n)]\}=x_1+x_1 -x_1-x_1+x_n=x_nx1​+{x1​−[x1​+(x1​−xn​)]}=x1​+x1​−x1​−x1​+xn​=xn​

(实际上就是第一次是减去它们两个之间的差,那么我们减去这个差不就是加上这个差,也就得到了另一个数。)

同理我们也可以把式子里所有的 xkx_kxk​ 换成 x1x_1x1​。

为什么要换成 x1x_1x1​ 呢,很显然如果还是当作 xix_ixi​,xkx_kxk​ 来用的话,我们并不知道 xix_ixi​,xkx_kxk​ 到底是谁 ~

而我们归为一类以后,只有一个变量 jjj ,我们只需要一次 for 循环即可。

最终得:

∑j(xj−x1)=K−x1\sum_{j}(x_j-x_1)=K-x_1j∑​(xj​−x1​)=K−x1​

我们可以设 aia_iai​ 表示 xi−x1x_i-x_1xi​−x1​,yiy_iyi​ 表示方程的未知数

这样就可以把上述方程转化为一个好看的丢番图方程(线性方程):

a1y1+a2y2+a3y3+⋯+anyn=K−x1a_1y_1+a_2y_2+a_3y_3+\cdots+a_ny_n=K-x_1a1​y1​+a2​y2​+a3​y3​+⋯+an​yn​=K−x1​

实际意义就是我们选 y1y_1y1​ 个a1a_1a1​(a1=x1−x1a_1=x_1-x_1a1​=x1​−x1​),y2y_2y2​ 个 a2a_2a2​(a2=x2−x1a_2=x_2-x_1a2​=x2​−x1​),⋯\cdots⋯,凑成 K−x1K-x_1K−x1​。(yiy_iyi​ 可以为 000 嘛)

我们只需要判断这个丢番图方程有解即可。若该方程有解,则说明最开始的式子 xi+∑j,k(xj−xk)=Kx_i+\sum_{j,k}(x_j-x_k)=Kxi​+∑j,k​(xj​−xk​)=K 有解(我们全程所有的式子的含义都没有变化,只是形式上的改变),说明 KKK 可以被通过题中所给的操作凑出来,也就是可以写到黑板上,也就是输出 YES

判断丢番图方程有解,很容易想到裴蜀定理:

设 a,ba,ba,b 是不全为零的整数,则存在整数x,yx,yx,y,使得ax+by=gcd⁡(a,b)ax+by = \gcd(a,b)ax+by=gcd(a,b)。 若mmm 是 gcd⁡(x,y)\gcd(x,y)gcd(x,y) 的倍数,方程同样有解(很显然,因为我们只需要两边同时乘上倍数即可。)

那么对于多个变量同样适用:

即:若 K−x1K-x_1K−x1​ 是 gcd⁡(x1−x1=0,x2−x1,x3−x1,⋯,xn−x1)\gcd(x_1-x_1=0,x_2-x_1,x_3-x_1,\cdots,x_n-x1)gcd(x1​−x1​=0,x2​−x1​,x3​−x1​,⋯,xn​−x1) 的倍数,则该丢番图方程有解,即 KKK 一定能够被凑出来写到黑板上,输出 YES

Code

非常简单的代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <vector>
#include <unordered_map>using namespace std;
typedef long long ll;
typedef int itn;
const int N = 5e5 + 7, mod = 1e9 + 7;
const ll INF = 1e18 + 7;ll x[N], n, m, t, k;int main()
{scanf("%lld", &t);while(t -- ) {scanf("%lld%lld", &n, &k);for(int i = 1; i <= n; ++ i)scanf("%lld", &x[i]);ll gcd = 0;for(int i = 1; i <= n; ++ i)gcd = __gcd(x[i] - x[1], gcd);if((k - x[1]) % gcd == 0)puts("YES");else puts("NO");}return 0;
}

看懂了叭 ~ 超级清晰有没有 (●ˇ∀ˇ●) 我真棒(

E - Nezzar and Binary String

A 有一个长度为 nnn 的二进制串 S,他想让自己最好的朋友 B 看,B将会用 qqq 天的时间来检查这个二进制串。
第 iii 天, B会在这一天早上检查这个二进制串的一个区间 [li,ri][l_i,r_i][li​,ri​] ,如果这个区间内的数字全部都是一致的,也就是全部都为 111 或者全部都为 000 ,B就会很开心,否则她就会不开心。
A为了使 B 每次检查都能变得开心,所以 A 会在每天 B 检查之后的当天晚上偷偷地去修改这个二进制串,使得 B 第二天检查的时候能够开心。为了不被 B 发现,A 每次只能修改今天早上 B 检查过的那个区间 [li,ri][l_i,r_i][li​,ri​],并且修改的字符的个数必须小于这个区间字符总数的一半,

与此同时,A还有一个任务,就是他希望可以通过这 qqq 天的修改,将二进制串 sss 变成 fff 。请问 A 能够在每天都保证 B 开心的同时,将二进制串 sss 修改为 fff 呢吗?

TTT 组数据,每组输入两个整数 nnn 和 qqq ,然后输入两个二进制串 sss 和 fff,接着输入 qqq 个区间。若 A 能在保证 B 每次都开心的前提下,完成这个任务,则输出 YES 否则输出 NO

1≤t≤2⋅105,1≤n≤2⋅105,0≤q≤2⋅1051 \le t \le 2 \cdot 10^5,1 \le n \le 2 \cdot 10^5,0 \le q \le 2 \cdot 10^51≤t≤2⋅105,1≤n≤2⋅105,0≤q≤2⋅105

4
5 2
00000
00111
1 5
1 3
2 1
00
01
1 2
10 6
1111111111
0110001110
1 10
5 9
7 10
1 7
3 5
6 10
5 2
10000
11000
2 5
1 3
YES
NO
YES
NO

Solution

我们本来按照题意的顺序,是她先查看,我们再修改,但是每次改都要受到之后她查看的区间的影响。

比如样例一:

第一天,她要看 [1,5][1,5][1,5] ,很开心走了 ~


第一天晚上,我可以改 [1,5][1,5][1,5] ,但是她明天要看 [1,3][1,3][1,3] ,所以我要先保证 [1,3][1,3][1,3] 是没有 0,10,10,1 同时存在的,再看能不能改 [1,5][1,5][1,5] 的其他元素使得s→fs\to fs→f,就很怪,当前的操作需要考虑之后的影响,我们还需要提前知道下一次的操作区间,就很奇怪。


第二天,她要看 [1,3][1,3][1,3],因为前一天晚上我的努力,她很开心的走了 ~


第二天晚上,我可以改 [1,3][1,3][1,3],明天她不看了,我就尽量改
[1,3][1,3][1,3] 使得s→fs\to fs→f

但是如果我们反过来,变成我们先改,她再看,也就是我们早上先改,晚上她再看,就很舒服。

第一天早上,我可以改 [1,3][1,3][1,3] ,因为她晚上要看 [1,3][1,3][1,3] ,所以我们只需要保证 [1,3][1,3][1,3] 是没有 0,10,10,1 同时存在的,再尽量朝着 f→sf\to sf→s 的方向改 [1,3][1,3][1,3] 。


第一天晚上,她看完 [1,3][1,3][1,3] ,开心地走了 ~


第二天早上,我可以改 [1,5][1,5][1,5],因为她晚上要看 [1,5][1,5][1,5],我们就保证 [1,5][1,5][1,5] 是没有 0,10,10,1 同时存在的,再尽量朝着 f→sf\to sf→s 的方向改 [1,5][1,5][1,5] ,因为这是最后一天了,我发现可以直接把 fff 变成 sss ,并且 sss 串全是 000,她看了一定很开心 (●ˇ∀ˇ●) ,我就这么做了


第二天,她要看 [1,5][1,5][1,5],因为早上我的努力,她开心地蹦蹦跳跳的走了 ~

其实正序做不只是很做起来,而且最关键的是,我们需要自己考虑最优解,这是非常困难的,而我们交换顺序对题目的答案不会造成任何影响,但是却会给我们的实现带来很大的方便,并且稍微分析一下就会发现,逆序做,不需要我们有任何操作空间。

使用逆向思维,可以帮助我们简化这个问题。然后我们来考虑怎么解决这个问题。

我们将会从 fff 出发,满足每次区间的一致性,致力于得到 sss 。

由于我们只有一个操作:修改定区间内的二进制串,并且每次最多只能修改个数小于区间长度的一半

实际上这就代表着我们只能有一种修改方式:将 0,10,10,1 中数量少的那一方修改为另一方,因为只有这样修改,我们才既能保证区间内的字符完全相同,又能保证修改的数量不超过区间长度的一半。

但是如果区间内 0,10,10,1 的数量相同,那就没办法修改了,无论偏袒哪一方都不能在修改小于区间长度的一半的条件下修改成同一个数字,直接输出 NO 即可。

然后我们发现我们会跟着这个区间的设定,一步一步没有任何操作空间地从头修改到最后,所以如果最后我们得到的字符串,如果就是 sss,那么恭喜你,成功完成了这个任务,你会很开心,她会很开心,我们都获得了快乐,直接输出 YES 即可。

同理,如果最后得到的不是 sss,那么抱歉,输出 NO

最后一个问题,如果快速地实现区间修改呢?

我们发现我们只需要有两个操作即可:区间全部修改(区间染色),区间查询(查询 0,10,10,1 的个数)

而数据很大:1≤t≤2⋅105,1≤n≤2⋅105,0≤q≤2⋅1051 \le t \le 2 \cdot 10^5,1 \le n \le 2 \cdot 10^5,0 \le q \le 2 \cdot 10^51≤t≤2⋅105,1≤n≤2⋅105,0≤q≤2⋅105,需要 O(Tqlogn)O(Tqlogn)O(Tqlogn) 的时间复杂度以及高超的卡常技巧,这一切无不指引着一个神奇的数据结构:线段树 ~

我们只需要维护区间和就行了,因为区间和就是1的个数。

由于需要区间查询,所以我们需要加上lazytag,就是我们修改的时候,区间是 000 还是 111。由于不能初始化为 000 ,重复了,所以我们记得初始化为 -1 就好。

然后就是很简单的代码了:

Code

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <vector>
#include <unordered_map>using namespace std;
typedef long long ll;
typedef int itn;
const int N = 3e5 + 7, mod = 1e9 + 7;
const ll INF = 1e18 + 7;int n, m, q;
int L[N], R[N];
char s[N], f[N];struct Tree
{int l, r;int sum;//维护 1 的个数, 因为 0 求和还是 0 和就是 1 的个数int lz;//1 / 0
}tr[N << 2];void push_up(int p)
{tr[p].sum = tr[p << 1].sum +  tr[p << 1 | 1].sum;
}void push_down(int p)
{if(tr[p].lz == -1) return ;tr[p << 1].lz = tr[p].lz;tr[p << 1 | 1].lz = tr[p].lz;tr[p << 1].sum = (tr[p << 1].r - tr[p << 1].l + 1) * tr[p].lz;//都是等于而不是加tr[p << 1 | 1].sum = (tr[p << 1 | 1].r - tr[p << 1 | 1].l + 1) * tr[p].lz;tr[p].lz = -1;
}void build(int p, int l, int r)//f
{tr[p].l = l, tr[p].r = r, tr[p].lz = -1;if(l == r) {tr[p].sum = f[l] - '0';tr[p].lz = -1;return ;}int mid = l + r >> 1;build(p << 1, l, mid);build(p << 1 | 1, mid + 1, r);push_up(p);
}void modify(int p, int l, int r, int val)
{if(tr[p].l >= l && tr[p].r <= r) {tr[p].sum = (tr[p].r - tr[p].l + 1) * val;//这里全部变成 1 ,sum 等于区间长度,全部变成 0, sum 就是 0tr[p].lz = val;return ;}push_down(p);int mid = tr[p].l + tr[p].r >> 1;if(l <= mid) modify(p << 1, l, r, val);if(r > mid) modify(p << 1 | 1, l, r, val);push_up(p);
}int query(int p, int l, int r)
{if(tr[p].l >= l && tr[p].r <= r) {return tr[p].sum;}push_down(p);int mid = tr[p].l + tr[p].r >> 1;int res = 0;if(l <= mid) res += query(p << 1, l, r);if(r > mid) res += query(p << 1 | 1, l, r);return res;
}bool check()
{for(int i = 1; i <= n; ++ i) {if(query(1, i, i) != s[i] - '0') {return false;}}return true;
}bool solve()
{scanf("%d %d", &n, &q);scanf("%s %s", s + 1, f + 1);for (int i = 1; i <= q; ++ i) {scanf("%d %d", &L[i], &R[i]);}build (1, 1, n);for(int i = q; i >= 1; -- i) {int l = L[i], r = R[i];int num1 = query(1, l, r);//sumint len = r - l + 1;if (num1 * 2 == len) return false;// 0 和 1 的长度相等 else if(num1 * 2 < len) modify(1, l, r, 0);else modify(1, l, r, 1);}if (!check()) {return false;}return true;
}itn t;int main()
{scanf("%d", &t);while (t -- ) {if (solve()) {puts("YES");}else {puts("NO");}}return 0;
}

F - Nezzar and Nice Beatmap(每日妙妙题精选)

每日妙妙题精选

Problem F Nezzar and Nice Beatmap

平面上有 nnn 个点(3≤n≤50003 ≤ n ≤ 50003≤n≤5000),请你找到一个点的排列,如 a1,a2,a3,a4,⋯,ana_1,a_2,a_3,a_4,\cdots,a_na1​,a2​,a3​,a4​,⋯,an​,使得这个排列中的任意相邻的三个点 ABC 组成的角 ∠ABC 是锐角(指 ∠a1a2a3,∠a2a3a4,∠a3a4a5,⋯,∠an−2an−1an∠a_1a_2a_3,∠a_2a_3a_4,∠a_3a_4a_5,\cdots, ∠a_{n-2}a_{n-1}a_n∠a1​a2​a3​,∠a2​a3​a4​,∠a3​a4​a5​,⋯,∠an−2​an−1​an​均为锐角)

Solution

题目要求的是输出一个排列,这个排列所有连续的三个点组成的角都是锐角。

我们首先考虑任意三个点

画一个草图:

我们发现一个钝角三角形,除了有钝角以外,还有两个锐角!

也就是说根据我们选择的顺序不同,是一定能找到一个锐角的排列的,也就是说一定是有解的。

我们发现钝角的对边是最长的那一条,如果我们不想要那个钝角,我们把这个最长的对边,变成我们的角边,不就不是钝角了嘛

思路来了,我们从任意一个点出发,在所有没有被选择过的点之中,找到一个跟它距离最远的点,连上,这样把所有最长的对边都变成角边,不就没有钝角了嘛

然后我们扩充到四个点


由于一定存在解,我们每次选择最远的点,这样就不会有钝角啦

考虑证明一下这个解法。

如果对于任意三个点,ABC ,我们已经确定选择了离 A 最远的 B ,离 B 最远的 C,那么 AC 一定小于 AB,那么 B 的对边一定不是最长的边,也就意味着 ∠ABC 一定是锐角(

Code

注意为了减少精度误差,因为题目中并没有提示精度误差的大小,说明不能有精度误差,所以我们求距离的时候就不用开根了,反正我们只需要比大小就行了。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <vector>
#include <unordered_map>using namespace std;
typedef long long ll;
typedef int itn;
const int N = 5e4 + 7, mod = 1e9 + 7;
const ll INF = 1e18 + 7;struct Point
{ll x, y;Point(ll x = 0, ll y = 0) : x(x), y(y) { }
}p[N];typedef Point Vector;
typedef Point POint;
int n, m;
ll ans[N], vis[N];itn main()
{scanf("%d", &n);for(int i = 1; i <= n; ++ i)scanf("%lld%lld", &p[i].x, &p[i].y);ans[1] = vis[1] = 1;for(int i = 2; i <= n; ++ i) {ll maxx = -1;ll id = -1;for(int j = 1; j <= n; ++ j) {if(vis[j] == 0) {if(id == -1 || maxx < (p[j].x - p[ans[i - 1]].x) * (p[j].x - p[ans[i - 1]].x) + (p[j].y - p[ans[i - 1]].y) * (p[j].y - p[ans[i - 1]].y)) {maxx = (p[j].x - p[ans[i - 1]].x) * (p[j].x - p[ans[i - 1]].x) + (p[j].y - p[ans[i - 1]].y) * (p[j].y - p[ans[i - 1]].y);id = j;}}}ans[i] = id, vis[id] = 1;}for(int i = 1; i <= n; ++ i) {printf("%lld ", ans[i]);}puts("");return 0;
}

官方题解:https://codeforces.com/blog/entry/87294(我还没看 )

Codeforces Round #698 (Div. 2)(A ~ F)6题全,超高质量题解)【每日亿题】2021/2/4相关推荐

  1. Codeforces Round #704 (Div. 2)(A ~ E)5题全 超高质量题解【每日亿题2 / 23】

    整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 目录 A.Three swimmers B.Card Deck C.Maximum width D.G ...

  2. Codeforces Round #698 (Div. 2) A-E解题报告与解法证明

    Codeforces Round #698 (Div. 2) A-E解题报告与解法证明 题目解法总体概括 A Nezzar and Colorful Balls #include <bits/s ...

  3. Codeforces Round #797 (Div. 3)无F

    Codeforces Round #797 (Div. 3)无F 这打的也太屎了,白天把G补了才知道简单的很,但f还是没头绪呜呜呜 Problem - A - Codeforces Given the ...

  4. Codeforces Round #701 (Div. 2) A ~ F ,6题全,超高质量良心题解【每日亿题】2021/2/13

    整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 目录 A - Add and Divide B - Replace and Keep Sorted C ...

  5. Codeforces Round #506 (Div. 3) 1029 F. Multicolored Markers

    CF-1029F 题意: a,b个小正方形构造一个矩形,大小为(a+b),并且要求其中要么a个小正方形是矩形,要么b个小正方形是矩形. 思路: 之前在想要分a,b是否为奇数讨论,后来发现根本不需要.只 ...

  6. Codeforces Round #698 (Div. 2)

    B题 题意:定义一个牛逼的数是这个数十进制中至少包含一个数d. 现在给定d和若干询问,每个询问一个x,问x能否分解成若干d构成的牛逼的数之和. 看起来挺难搞的,不能快速的判断是否是牛逼的数而且也不能很 ...

  7. Codeforces Round #827 (Div. 4) D - F

    D. Coprime time limit per test 3 seconds memory limit per test 256 megabytes input standard input ou ...

  8. Educational Codeforces Round 110 div.2 A~F题解

    视频讲解:BV1254y137Rn A. Fair Playoff 题目大意 有 444 位选手参加比赛,第 iii 位选手的水平为 si(1≤si≤100)s_i(1 \leq s_i \leq 1 ...

  9. Codeforces Round #698 (Div. 2) D. Nezzar and Board(一步步推出来,超级清晰,不猜结论,看不懂来打我 ~ 好题 )

    整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! D - Nezzar and Board Problem A Nezzar and Board 我们在黑板上写了 nnn 个数,x1,x2 ...

最新文章

  1. html 表格点击修改全部替换成文本_excel表格计算一个数据在总值中的占比
  2. .NET软件开发, 你应该知道 (整)
  3. html的选择器child,css child选择器妙用
  4. WMI介绍和应用查询硬件信息(硬盘信息、主板信息、BIOS信息、显示器信息、网络适配器、CPU信息)
  5. edge css兼容,CSS输入错误样式在Edge浏览器中无法正确显示
  6. c语言中保存的文件时怎么换行,关于文件操作,碰到空格就换行
  7. android 开发种子文件,IT之家学院:如何制作种子文件和磁力链接
  8. SAP中物料需求计划不考虑库存策略应用案例
  9. 证件照缩小为20k大小
  10. 2017年工作总结--初出茅庐
  11. 计算机dns服务器错误或不存在,找不到服务器或 DNS 错误,详细教您找不到服务器或dns错误怎么解决...
  12. matlab求时频分布图,Matlab时频分析TFD程序集(时频分布、chirplet分解、变形分数傅立叶变换)源代码...
  13. PHP监控网站运行状态
  14. 计算机历史博物馆观后感:阿达·洛芙莱斯生平1
  15. 知乎神回复:计算机专业学成什么样,才算“大学没白读“?
  16. 杨老师课堂之JavaScript定时器_农夫山泉限时秒杀案例
  17. LabVIEW与MATLAB联合编程之使用dll库文件连接
  18. 求是追梦--------一位计算机专业硕士毕业生的求职经历和感想
  19. ASp.net动态加载js和css文件
  20. 关于 打印机可打印无法扫描报错“使用该设备需要WIA驱动程序,请从安装CS或从...” 的解决方法

热门文章

  1. 3D视觉应用开发--机器人3D互动四大技术难点分析
  2. 惊呆了!这样可以将Numpy加速700倍!
  3. 图像分类和目标检测技术有什么区别?
  4. 关系型数据库-三范式
  5. Java反射 - 动态类加载和重载
  6. 利用json模块解析dict报错找不到attribute 'dumps'[python2.7]
  7. 非技术成本继续困扰光伏产业
  8. Tomcat Servlet
  9. 阿里感悟(九)-如何才能晋升
  10. 分布式数据库灵活存储机制与应用实践