【题目链接】

https://agc027.contest.atcoder.jp/

A

【题解】

题意: 是把xxx个糖果分给nnn个人,一个人如果恰好分到aia_{i}ai​个糖果就会高兴。求最多使多少个人高兴。

题解: 一定是优先满足需求小的人,特判有额外的剩余糖果。

时间复杂度:O(NlogN)O(NlogN)O(NlogN)

【代码】

# include <bits/stdc++.h>
# define    ll      long long
using namespace std;
const int inf = 0x3f3f3f3f, INF = 0x7fffffff;
const ll  infll = 0x3f3f3f3f3f3f3f3fll, INFll = 0x7fffffffffffffffll;
int read(){int tmp = 0, fh = 1; char ch = getchar();while (ch < '0' || ch > '9'){ if (ch == '-') fh = -1; ch = getchar(); }while (ch >= '0' && ch <= '9'){ tmp = tmp * 10 + ch - '0'; ch = getchar(); }return tmp * fh;
}
const int N = 110;
int n, sum, num[N], ans;
int main(){n = read(), sum = read();for (int i = 1; i <= n; i++) num[i] = read();sort(num + 1, num + n + 1);ans = 0;while (ans < n){if (sum >= num[ans + 1]){sum -= num[++ans]; }else break;}if (ans == n && sum != 0) ans--;printf("%d\n", ans);return 0;
}

B

【题解】

题意: 一条数轴上有nnn个垃圾,全在正半轴上。OOO点是垃圾桶。现在有一个机器人,一开始在OOO点。它每移动格的代价为(k+1)2(k+1)^2(k+1)2,kkk为当前携带的垃圾数量。在移动到与垃圾坐标相同时可以捡起垃圾,每次捡起/扔掉垃圾都要xxx的代价(不能再非OOO点扔垃圾)。求最小代价。

题解: 考试时以为一次一定取相邻的一段,这显然是错的。

考虑贪心,行走的路线一定是先是走到最远的点,在回来时不断带上其他的点。

一个点(坐标p),如果在一趟路程中是第iii个取完的,那么它的代价是:

(i∗2+3)∗p(i*2+3)*p(i∗2+3)∗p如果i=1i=1i=1

(i∗2+1)∗p(i*2+1)*p(i∗2+1)∗p如果i&gt;1i&gt;1i>1

所以远的点被取的位置一定在近的点之前。

那么我们枚举一共走的次数,那么取的顺序是唯一确定的。

于是就有了一个O(N2)O(N^2)O(N2)的做法。

考虑优化由于⌊i/j⌋\lfloor i/j\rfloor⌊i/j⌋只有i\sqrt{i}i​种取值,所以所有的垃圾被取的位置只会变换NlogNNlogNNlogN次。

时间复杂度O(NlogN)O(NlogN)O(NlogN)

【代码】

# include <bits/stdc++.h>
# define    ll      long long
using namespace std;
const int inf = 0x3f3f3f3f, INF = 0x7fffffff;
const ll  infll = 0x3f3f3f3f3f3f3f3fll, INFll = 0x7fffffffffffffffll;
int read(){int tmp = 0, fh = 1; char ch = getchar();while (ch < '0' || ch > '9'){ if (ch == '-') fh = -1; ch = getchar(); }while (ch >= '0' && ch <= '9'){ tmp = tmp * 10 + ch - '0'; ch = getchar(); }return tmp * fh;
}
const int N = 201000;
int n;
ll x, p[N], ans = infll, sum[N];
int main(){//  freopen(".in", "r", stdin);
//  freopen(".out", "w", stdout);n = read(), x = read();for (int i = 1; i <= n; i++) p[i] = read();int lim = n / 1000;sum[0] = n * x;for (int i = 1; i <= n; i++) sum[i] = x; sum[1] += p[n] * 5; for (int i = 1; i <= n; i++){int id = n - i + 1, nxt = 1, k = i - 1;bool flag = false;for (int j = 1; j <= k; j = nxt + 1){nxt = k / (k / j);int w = k / j + 1;if (j != 1 && flag == true){int las = k / (j - 1) + 1;sum[j] = sum[j] - p[id] * (2 * las + 1);}if (j >= lim || k / j == 1){sum[j] = sum[j] + p[id] * (2 * w + 1);flag = true;}} } for (int i = 1; i <= n; i++){sum[i] += sum[i - 1]; if (i >= lim) ans = min(ans, sum[i]);}printf("%lld\n", ans);return 0;
}

C

【题解】

题意: 有一个图,每个节点有一个字母aaa或bbb,在这个图上,一条路径表示一个字符串(把点上的字母连起来)。现在问这个图上的所有路径是否能表示所有的只用aaa与bbb构成的字符串。

题解: 原问题等价于寻找一个由重复的a−a−b−ba-a-b-ba−a−b−b构成的环,即这个环上每个点都与环上的一个aaa与一个bbb相邻。

考虑把一个节点拆成两种,一种是下一步要与当前点不同,另一种是下一步要与当前点相同。然后在新图上跑tarjan,如果有环,则有解。

时间复杂度O(N+M)O(N+M)O(N+M)

【代码】

# include <bits/stdc++.h>
# define    ll      long long
using namespace std;
const int inf = 0x3f3f3f3f, INF = 0x7fffffff;
const ll  infll = 0x3f3f3f3f3f3f3f3fll, INFll = 0x7fffffffffffffffll;
int read(){int tmp = 0, fh = 1; char ch = getchar();while (ch < '0' || ch > '9'){ if (ch == '-') fh = -1; ch = getchar(); }while (ch >= '0' && ch <= '9'){ tmp = tmp * 10 + ch - '0'; ch = getchar(); }return tmp * fh;
}
const int N = 200010;
struct Edge{int data, next;
}e[N * 2];
int use[N][2], head[N], flag, n, m, place;
char s[N];
void build(int u, int v){e[++place].data = v; e[place].next = head[u]; head[u] = place;
}
void dfs(int x, int tag){use[x][tag] = 1;for (int ed = head[x]; ed != 0; ed = e[ed].next){if (use[e[ed].data][tag ^ 1] == 2) continue;if (tag == 1){if (s[e[ed].data] == s[x]) continue;if (use[e[ed].data][0] == false)dfs(e[ed].data, 0);else flag = true;}else {if (s[e[ed].data] != s[x]) continue;if (use[e[ed].data][1] == false)dfs(e[ed].data, 1);else flag = true;}}use[x][tag] = 2;
}
int main(){//  freopen(".in", "r", stdin);
//  freopen(".out", "w", stdout);n = read(), m = read();scanf("\n%s", s + 1);for (int i = 1; i <= m; i++){int u = read(), v = read();build(u, v);build(v, u);}for (int i = 1; i <= n; i++){if (use[i][0] == 0) dfs(i, 0);if (use[i][1] == 0) dfs(i, 0); }if (flag)printf("Yes\n");else printf("No\n");return 0;
}

D

【题解】

题面: 构造一个n∗nn*nn∗n的矩阵,其中的每一个数不能超过101510^151015。使得存在一个数xxx使任意相邻的两个数中大%小=x大\%小=x大%小=x

题解: 考虑·一个x=1x=1x=1的情况,若所有(x+y)%2=0(x+y)\%2=0(x+y)%2=0的格子中各填一个不同的素数,在其他格子中填相邻四个数的乘积(lcm)+1。那么这一定是一个合法解。但是这样做值域不够。

考虑(x+y)%2=0(x+y)\%2=0(x+y)%2=0的格子,对于左上-右下的对角线我们用前500个素数,左下-右上的对角线我们用第501到第1000个素数。格子中的数为对应的两条对角线上对应质数的积。那么每个格子的值一定不同。其他格子仍然为相邻四格的lcm,由于相邻四格中相邻两个一定有一条对角线相同,所以lcm为相邻四格的乘积\sqrt{相邻四格的乘积}相邻四格的乘积​

这样的话,格子中最大的数不会超过4个第1000个质数的乘积,符合条件。

【代码】

# include <bits/stdc++.h>
# define    ll      long long
using namespace std;
const int inf = 0x3f3f3f3f, INF = 0x7fffffff;
const ll  infll = 0x3f3f3f3f3f3f3f3fll, INFll = 0x7fffffffffffffffll;
int read(){int tmp = 0, fh = 1; char ch = getchar();while (ch < '0' || ch > '9'){ if (ch == '-') fh = -1; ch = getchar(); }while (ch >= '0' && ch <= '9'){ tmp = tmp * 10 + ch - '0'; ch = getchar(); }return tmp * fh;
}
const int N = 100010;
ll mp[520][520];
int p[N], pnum;
bool use[N];
int n;
void prep(int n){use[1] = true;for (int i = 2; i <= n; i++){if (use[i] == false) p[++pnum] = i;for (int j = 1; 1ll * i * p[j] <= n && j <= pnum; j++){use[p[j] * i] = true;if (i % p[j] == 0) break;}}
}
int main(){n = read();prep(100000);for (int i = 1; i <= n + 2; i++)for (int j = 1; j <= n + 2; j++)if ((i + j) % 2 == 0){int p1 = (i + j) / 2, p2 = (i - j) / 2 + 760;mp[i][j] = p[p1] * p[p2];} for (int i = 2; i <= n + 1; i++)for (int j = 2; j <= n + 1; j++)if ((i + j) % 2 == 1){mp[i][j] = mp[i - 1][j] * mp[i + 1][j];mp[i][j] += 1;}for (int i = 2; i <= n + 1; i++)for (int j = 2; j <= n + 1; j++)printf("%lld%c", mp[i][j], (j == n + 1) ? '\n' : ' ');return 0;
}

E

【题解】

题面: 给定串SSS(∣S∣≤2e5|S|\leq 2e5∣S∣≤2e5),SSS中包含a,ba,ba,b,有两种操作:

1.将连续的两个aaa变成一个bbb。

2.将连续的两个bbb变成一个aaa。

可以在任意时刻终止操作,求最后的串有几种可能。

题解: 记Si,jS_{i,j}Si,j​表示SSS从iii到jjj的子串。

不妨把aaa看做111,把bbb看做222。记Pi,jP_{i,j}Pi,j​表示(∑k=ijSk)%3(\sum_{k=i}^{j}S_{k})\%3(∑k=ij​Sk​)%3。

那么Si,jS_{i,j}Si,j​能转化为一个字符xxx的条件是:

1.i=ji=ji=j或Si,jS_{i,j}Si,j​中有相邻两个字符相同。

2.Pi,j=PxP_{i,j}=P_xPi,j​=Px​

条件1保证可以进行操作,2保证最后变成的数相同。

如果我们想要得到一个串TTT,我们尽量取最小的前缀去得到它。具体来讲,用尽量少的字母得到TTT中第一个字符,以此类推。

那么最后要么无法组成TTT,要么刚好组成,要么会剩下一段SSS的后缀Si,nS_{i,n}Si,n​。给出结论:

如果Pi,n=0P_{i,n}=0Pi,n​=0且SSS中存在两个相邻相同的字符,那么TTT可以被构成。

讲一个简略的证明:

设构成TTT的最后一个字符的一段为Sj,i−1S_{j,i-1}Sj,i−1​。

如果Sj,nS_{j,n}Sj,n​有连续两个相同的字符,那么这一段可以简单合并为TTT的最后一个字符。

如果没有说明j=i−1j=i-1j=i−1且Sj,nS_{j,n}Sj,n​字符两两交替出现。

设kkk为Sj=Sj+1S_{j}=S_{j+1}Sj​=Sj+1​的kkk的最大值。字符yyy为kkk所在一段在TTT中构成的字符。

分为两种情况:

1.kkk单独构成yyy。由于Pi,n=0P_{i,n}=0Pi,n​=0所以TTT的末尾字符等于SSS的末尾字符。所以一直往后合并,一定有一个时刻相同。

2.kkk不单独构成yyy,先把当前段合并到只剩两个字符,再一直往后合并即可。

证毕。

接下来就是一个简单的dp了。记fif_{i}fi​表示SSS中的前iii位能恰好构成多少种TTT。枚举下一个字符可行的最短距离,用set维护即可。

时间复杂度O(NlogN)O(NlogN)O(NlogN)

【代码】

# include <bits/stdc++.h>
# define    ll      long long
using namespace std;
const int inf = 0x3f3f3f3f, INF = 0x7fffffff;
const ll  infll = 0x3f3f3f3f3f3f3f3fll, INFll = 0x7fffffffffffffffll;
int read(){int tmp = 0, fh = 1; char ch = getchar();while (ch < '0' || ch > '9'){ if (ch == '-') fh = -1; ch = getchar(); }while (ch >= '0' && ch <= '9'){ tmp = tmp * 10 + ch - '0'; ch = getchar(); }return tmp * fh;
}
const int N = 200010, P = 1e9 + 7;
char s[N];
int num[N], f[N][3][2], n, tag[N], pre[N], ans, sam[N];
set <int> mp[3];
int main(){scanf("\n%s", s + 1);n = strlen(s + 1);for (int i = 1; i <= n; i++) num[i] = s[i] - 'a' + 1;for (int i = 1; i <= n; i++){if (sam[i - 1] >= i) sam[i] = sam[i - 1];else {sam[i] = n;for (int j = i; j < n; j++)if (num[j] == num[j + 1]){sam[i] = j;break;}}}for (int i = n; i >= 1; i--) tag[i] = tag[i + 1] | (num[i] == num[i - 1]); for (int i = 1; i <= n; i++){pre[i] = (pre[i - 1] + num[i]) % 3;mp[pre[i]].insert(i);}mp[0].insert(n + 1), mp[1].insert(n + 1), mp[2].insert(n + 1);int cnt = 1;int j = *mp[1].upper_bound(sam[1]);if (num[1] == 1) j = 1;if (j <= n){if (j > 1) f[j][1][0] = (f[j][1][0] + cnt) % P;else f[j][1][1] = (f[j][1][1] + cnt) % P;}j = *mp[2].upper_bound(sam[1]);if (num[1] == 2) j = 1;if (j <= n){if (j > 1) f[j][2][0] = (f[j][2][0] + cnt) % P;else f[j][2][1] = (f[j][2][1] + cnt) % P;}for (int i = 1; i < n; i++){for (int j = 1; j <= 2; j++){int cnt = (f[i][j][0] + f[i][j][1]) % P;int k = *mp[(pre[i] + 1) % 3].upper_bound(sam[i + 1]);if (num[i + 1] == 1) k = i + 1;if (k <= n){if (k > i + 1 || j == 1) f[k][1][0] = (f[k][1][0] + cnt) % P;else f[k][1][1] = (f[k][1][1] + cnt) % P;}k = *mp[(pre[i] + 2) % 3].upper_bound(sam[i + 1]);if (num[i + 1] == 2) k = i + 1;if (k <= n){if (k > i + 1 || j == 2) f[k][2][0] = (f[k][2][0] + cnt) % P;else f[k][2][1] = (f[k][2][1] + cnt) % P;}}}for (int i = 1; i <= n; i++)if (pre[n] - pre[i] == 0){ans = ((ans + f[i][1][1]) % P + f[i][2][1]) % P;ans = ((ans + f[i][1][0]) % P + f[i][2][0]) % P;}if (sam[1] == n)printf("%d\n", 1);else printf("%d\n", ans);return 0;
}

F

【题解】

题面: 给定两棵树AAA,BBB,有nnn个节点。每次可以选择AAA中的一个叶子节点,把与它相连的边删去。再任意选择一个节点与它连边。一个点只能被操作一次。求出把AAA变为BBB的最小步数。n≤50n\leq 50n≤50

题解: 先考虑一种简单的情况:AAA中含有不动点,即不会被操作的点。

那么我们固定这个点xxx,再找出这个点AAA与BBB以xxx为根的最大相同子树。在这个子树上的点都是不能被操作的。其他的点一定要操作。如果一个需要操作的点uuu,它一开始与vvv相连,在BBB中它的父亲为v′v'v′,记tit_{i}ti​为iii号节点被操作的时间,那么以下两个条件需要被满足:

1.若v′v'v′为需要操作的点,那么tv′&lt;tut_{v'}&lt;t_{u}tv′​<tu​

2.若vvv为需要操作的节点,那么tu&lt;tvt_{u}&lt;t_{v}tu​<tv​

若存在满足条件的拓扑序,那么AAA可以变为BBB。步数就是需要操作的点的数目。

接下来考虑没有不动点的情况,我们枚举第一步选的叶子节点及它连的边。由于每个点只能操作一次,那么这个点在这之后一定是不动点。用之前的算法就可以了。

时间复杂度:枚举O(N2)O(N^2)O(N2),判断O(N)O(N)O(N)。总复杂度O(N3)O(N^3)O(N3)

【代码】

# include <bits/stdc++.h>
# define    ll      long long
using namespace std;
const int inf = 0x3f3f3f3f, INF = 0x7fffffff;
const ll  infll = 0x3f3f3f3f3f3f3f3fll, INFll = 0x7fffffffffffffffll;
int read(){int tmp = 0, fh = 1; char ch = getchar();while (ch < '0' || ch > '9'){ if (ch == '-') fh = -1; ch = getchar(); }while (ch >= '0' && ch <= '9'){ tmp = tmp * 10 + ch - '0'; ch = getchar(); }return tmp * fh;
}
const int N = 61;
vector <int> ea[N], eb[N], e[N];
int ua[N], va[N], ub[N], vb[N], deg[N], use[N], cnt, da[N], db[N], tag[N], n, q[N];
void dfs(int x, int fa){use[x] = true; cnt++;for (unsigned eda = 0, edb = 0; eda < ea[x].size() && edb < eb[x].size(); ){//  printf("%d %d\n", ea[x][eda], eb[x][edb]);if (ea[x][eda] == eb[x][edb] && ea[x][eda] != fa) dfs(ea[x][eda], x);if (edb >= eb[x].size() - 1 || (eda < ea[x].size() - 1 && ea[x][eda + 1] <= eb[x][edb + 1]))eda++; else edb++; }
}
void foda(int x, int fa){da[x] = fa;for (unsigned ed = 0; ed < ea[x].size(); ed++)if (ea[x][ed] != fa) foda(ea[x][ed], x);
}
void fodb(int x, int fa){db[x] = fa;for (unsigned ed = 0; ed < eb[x].size(); ed++)if (eb[x][ed] != fa) fodb(eb[x][ed], x);
}
int check(int x){memset(use, 0, sizeof(use)); memset(tag, 0, sizeof(tag));for (int i = 1; i <= n; i++) ea[i].clear(), eb[i].clear(), e[i].clear();for (int i = 1; i < n; i++){ea[ua[i]].push_back(va[i]);ea[va[i]].push_back(ua[i]);}for (int i = 1; i < n; i++){eb[ub[i]].push_back(vb[i]);eb[vb[i]].push_back(ub[i]);}for (int i = 1; i <= n; i++){sort(ea[i].begin(), ea[i].end());sort(eb[i].begin(), eb[i].end());}cnt = 0; dfs(x, 0);cnt = n - cnt;foda(x, 0); fodb(x, 0);for (int i = 1; i <= n; i++){if (use[i] == true) continue;if (use[da[i]] == false) e[i].push_back(da[i]), tag[da[i]]++;if (use[db[i]] == false) e[db[i]].push_back(i), tag[i]++;}int pl = 1, pr = 0, tmp = 0;for (int i = 1; i <= n; i++) if (tag[i] == 0) q[++pr] = i; while (pl <= pr){int x = q[pl++]; tmp++;for (unsigned i = 0; i < e[x].size(); i++){tag[e[x][i]]--;if (tag[e[x][i]] == 0) q[++pr] = e[x][i];}}if (tmp == n) return cnt;else return inf;
}
int main(){for (int opt = read(); opt--; ){n = read();memset(deg, 0, sizeof(deg));for (int i = 1; i < n; i++){ua[i] = read(), va[i] = read();deg[ua[i]]++; deg[va[i]]++;}for (int i = 1; i < n; i++)ub[i] = read(), vb[i] = read();int flag = inf;for (int i = 1; i <= n; i++)flag = min(check(i), flag);for (int i = 1; i <= n; i++){if (deg[i] != 1) continue;for (int j = 1; j <= n; j++){if (i == j) continue;for (int k = 1; k < n; k++){if (ua[k] == i){int tmp = va[k];va[k] = j;flag = min(flag, check(i) + 1);va[k] = tmp;break;}if (va[k] == i){int tmp = ua[k];ua[k] = j;flag = min(flag, check(i) + 1);ua[k] = tmp;break;}}}}if (flag == inf)printf("-1\n");else printf("%d\n", flag); } return 0;
}

[atcoder]AtCoder Grand Contest 027题解相关推荐

  1. AtCoder Grand Contest 028题解

    C - Min Cost Cycle 思路好6啊,考试想了半天都没有想出来. 一直在想一个错误的贪心算法. 首先,我们把加一条权值为$min(Ax,By)$的边变成两条权值分别为$Ax,By$的边. ...

  2. AtCoder Grand Contest 013 题解

    A - Sorted Arrays 贪心,看看不下降和不上升最长能到哪,直接转移过去即可. 1 //waz 2 #include <bits/stdc++.h> 3 4 using nam ...

  3. Atcoder Grand Contest 005 题解

    A - STring 用一个栈模拟即可. //waz #include <bits/stdc++.h>using namespace std;#define mp make_pair #d ...

  4. 【每日亿题#12】AtCoder Grand Contest 021 (A ~ F)全部题解

    整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 文章目录 AtCoder Grand Contest 021 题解 A. Digit Sum 2 B. ...

  5. AtCoder题解——AtCoder Grand Contest 048——A - atcoder < S

    题目相关 题目链接 AtCoder Grand Contest 048 A 题,https://atcoder.jp/contests/agc048/tasks/agc048_a. Problem S ...

  6. AtCoder题解 —— AtCoder Grand Contest 050 —— B - Three Coins —— 动态规划

    题目相关 题目链接 AtCoder Grand Contest 050 B 题,https://atcoder.jp/contests/agc050/tasks/agc050_b. Problem S ...

  7. AtCoder Grand Contest 008: Contiguous Repainting(思维)

    Contiguous Repainting 时间限制: 2 Sec  内存限制: 256 MB 提交: 69  解决: 22 [提交][状态][讨论版][命题人:admin] 题目描述 There a ...

  8. AtCoder Grand Contest 017

    AtCoder Grand Contest 017 A - Biscuits 有\(n\)个数,问有多少个集合的数的和模\(2\)余\(P\). 随便\(dp\)一下就好了. #include< ...

  9. AtCoder Beginner Contest 197 题解(A ~ F)

    整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 目录 A - Rotate B - Visibility C - ORXOR D - Opposite ...

最新文章

  1. mysql 8.0免安装配置_Mysql8.0免安装包配置方法
  2. Spring Boot中表格的请求以及表格界面的显示
  3. 4.6.1 定义工作区
  4. 十分钟轻松搞懂CSS的五大定位方式!(建议收藏)
  5. python书写跨越多行的字符串的两种方式
  6. 过目不忘JS正则表达式(转)
  7. vue返回上一页面时回到原先滚动的位置
  8. android上下居中,android Spinner:在spinner中垂直居中文本
  9. win10桌面右键一直转圈_不多占1M内存的win10桌面美化
  10. python怎么创建桌面快捷方式_Python3创建RIDE桌面快捷方式的另一种方法
  11. 线性代数 --- 向量的内积与正交(垂直),Orthogonal Vectors
  12. 一朵花的组成结构图_请问一朵完整的花由哪几部分组成
  13. Python,还有一些鲜为人知的特性!你知道吗?
  14. vtk中的win32窗口
  15. ORACLE 正负数分开排序 SQL
  16. 驱动辅助资料(工具,目录,指令,debug经验)
  17. 英语单词 One 个人 2. 出生和死亡
  18. 用 JavaScript 实现手势库 - 实现监听逻辑【前端组件化】
  19. 互联网出海现在还是风口么?
  20. Vue.js 父子组件通信的十种方式;告诉世界前端也能做 AI;你可能不知道的14个JavaScript调试技巧...

热门文章

  1. Java Jsoup详解
  2. 混合开发经验之谈-3TH
  3. Linux蓝牙拨号,linux debian 手机usb线和蓝牙拨号上网
  4. aspnet从入门到精通第4版资源_ASP.NET 4从入门到精通 中文PDF高清电子版
  5. iOS 8下地图使用
  6. LabVIEW字符串正常显示与十六进制显示相互强制转换
  7. python连接微信接口开发教程_Python调用微信公众平台接口操作示例
  8. raptor流程图赋值语句_raptor流程图编程
  9. # 个人日记-《困在时间里的父亲》电影观后感-2021-07-11
  10. 图片缩放、旋转、加水印