2017-2018 ACM-ICPC, NEERC, Southern Subregional Contest A E F G H I K M
// 深夜补水题,清早(雾)写水文
A. Automatic Door
题意
\(n(n\leq 1e9)\)个\(employee\)和\(m(m\leq 1e5)\)个\(client\)要进门,\(employee\)进门的时刻为\(a,2a,...,.na\),\(client\)进门的时间则由输入数据给定。
这个门很厉害,是个自动门。如果第\(k\)时刻有人要进门,那么它会在第\(k\)时刻打开,在第\(k+d\)时刻再关闭,在\([k,k+d]\)时刻要进门的人都能在这段期间进门。
问门会打开多少次。
思路
因为\(n\)太大了,所以显然不可能将\(n+m\)个数存下来再扫描,况且\(employee\)进门的时间还是有规律的。
所以可以考虑根据每个\(client\)进门的时间去算前一段时间内为了满足\(employee\)以及当前这个\(client\)进门的需求开了多少次门,以及这个门一直开到了什么时候。
维护当前门可以持续开到的时间\(last\)搞一搞即可。
假设上次处理到的时间为\(last\),当前\(client\)要进门的时间为\(t\).
如果\(last\geq t\),那么直接continue;
若存在\(k\)满足\(last\lt k_{min}a\lt ...\lt k_{max}a\leq t\),说明中间有\([min,max]个employee\)要进门。又根据\(a\)与\(d\)的关系知门每开一次能让\(k=d/a+1\)个\(employee\)进门,再讨论一下为最后一个\(employee\)开的门能否让当前的\(client\)也进门即可。这样就求出了这一整段的开门次数以及维护了当前的达到的\(last\)时间。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main() {
LL n, m, a, d;
scanf("%I64d%I64d%I64d%I64d", &n, &m, &a, &d);
LL k = d/a+1, ans = 0, last = 0;
for (int i = 0; i < m; ++i) {
LL t;
scanf("%I64d", &t);
if (t <= last) continue;
if (t < a) { last = t + d, ++ans; continue; }
LL kmin = last/a+1, kmax = t/a;
if (kmin <= kmax && kmin <= n) {
kmax = min(kmax, n);
LL delk = kmax-kmin+1;
LL cnt = ceil(1.0*delk / k)-1;
ans += cnt;
kmin += cnt*k;
if (kmin*a+d >= t) last = kmin*a+d, ++ans;
else last = t+d, ans += 2;
}
else last = t + d, ++ans;
}
LL b = last / a + 1;
if (b <= n) ans += ceil(1.0*(n-b+1) / k);
printf("%I64d\n", ans);
return 0;
}
E. Field of Wonders
题意
规定游戏规则为:有一个未知字符串,玩家操作若干次,每次操作猜一个字母,如果这个字符串里面有这个字母,那么所有位置的该字符都会显示出来。
现在给定一个游戏进行到中途某一阶段时该字符串变成的样子(数据保证至少还有一个位置的字符没有被猜出来),再给定一个字符串的集合。问下一步玩家有多少种猜的选择,使得保证能使至少一个位置的字符再多显示出来。
思路
题意即为,问有多少个 还没猜过的 字符 在 所有与当前局面匹配的字符串中 全部出现过。
直接统计即可。
注意
判断与当前局面匹配的字符串时,需要进行两个判断:
首先,很显然的,要求猜出来的显示出来的(即非*
处对应的)字符是一样的。
其次,因为游戏规定,一旦猜中某个字符那么所有位置的该字符都会显示出来,所以未猜出来的(*
处对应的)字符绝不可能与之前猜出来的(非*
处对应的)字符相同。
// 感叹一句样例真心良心
Code
#include <bits/stdc++.h>
#define maxn 1010
using namespace std;
typedef long long LL;
char s[maxn], rec[maxn];
int blank[maxn], n, cnt[maxn];
bool exist[maxn], exist2[maxn];
bool match(const char* s1, const char* s2) {
memset(exist2, 0, sizeof(exist2));
for (int i = 0; i < n; ++i) {
if (s2[i] != '*') exist2[s2[i]] = true;
}
for (int i = 0; i < n; ++i) {
if (s2[i] == '*' && exist2[s1[i]]) return false;
}
for (int i = 0; i < n; ++i) {
if (s2[i] == '*') continue;
if (s1[i] != s2[i]) return false;
}
return true;
}
int main() {
scanf("%d", &n);
scanf("%s", s);
int m, tot = 0;
for (int i = 0; s[i]; ++i) if (s[i] == '*') blank[tot++] = i;
scanf("%d", &m);
int total = 0;
for (int i = 0; i < m; ++i) {
scanf("%s", rec);
memset(exist, 0, sizeof(exist));
if (match(rec, s)) {
++total;
for (int j = 0; j < tot; ++j) {
exist[rec[blank[j]]] = true;
}
for (int j = 'a'; j <= 'z'; ++j) {
if (exist[j]) ++cnt[j];
}
}
}
int ans = 0;
for (int i = 'a'; i <= 'z'; ++i) {
if (cnt[i] >= total) ++ans;
}
printf("%d\n", ans);
return 0;
}
F. Lost in Transliteration
题意
用拉丁字母书写波兰姓名可能会有歧义,体现在两点上:
- \(kh\leftrightarrow h\)
- \(oo\leftrightarrow u\)
现给出一串名字,问有多少不同的名字。
注意,变化是有后续影响的,即可以接连变化的。
思路
最方便的做法是:
将所有的\(kkkk……kkh\)替换成\(h\),将所有的\(u\)替换成\(oo\).
前者显而易见,为什么后者是将所有的\(u\)替换成\(oo\),而不是反之呢?
如果反之,将\(oo\)都化成\(u\). 考虑奇数个\(o\)的情况,它事实上对应了\(n/2+1\)种串,每两个\(o\)一合并,最终多出来的那个\(u\)可能出现在第一个到最后一个之间的任意一个位置上。就不方便比较了。
// 再感叹一句样例真心良心
采用将所有的\(kkkk……kkh\)替换成\(h\),将所有的\(u\)替换成\(oo\),就保证了结果的唯一性。
// 一个小注意点,\(u\)替换成\(oo\)后字符串的长度可能翻倍,数组要开两倍。
最后排个序数一数即可。
// 还学到了字符串数组特殊的排序技巧...。
Code
#include <bits/stdc++.h>
#define maxl 110
#define maxn 410
using namespace std;
typedef long long LL;
char s[maxl];
struct Array {
char data[maxl];
char& operator [] (const int idx) { return data[idx]; }
}ss[maxn];
bool cmp(Array a, Array b) {
return strcmp(a.data, b.data) < 0;
}
int main() {
int n;
scanf("%d", &n);
for (int i = 0; i < n; ++i) {
scanf("%s", s);
int len = strlen(s), p = 0;
while (p < len) {
if (s[p] == 'k') {
int j = p;
while (j < len && s[j] == 'k') ++j;
if (j == len || s[j] != 'h') {
for (; p < j; ++p) ss[i][++ss[i][0]] = 'k';
}
else if (s[j] == 'h') ss[i][++ss[i][0]] = 'h', ++j;
p = j;
}
else if (s[p] == 'u') {
ss[i][++ss[i][0]] = 'o';
ss[i][++ss[i][0]] = 'o';
++p;
}
else ss[i][++ss[i][0]] = s[p++];
}
ss[i][ss[i][0]+1] = '\0';
}
sort(ss, ss+n, cmp);
int ans = 1;
for (int i = 1; i < n; ++i) {
if (cmp(ss[i-1], ss[i]) == 1) ++ans;
}
printf("%d\n", ans);
return 0;
}
G. Orientation of Edges
题意
给定一张图,\(n\)个点,\(m\)条边,若干条有向边,若干条无向边,且有重边。现要求给所有的无向边定向,使得从某个给定点\(s\)出发能够访问到的点的总数最大/最小。输出个数及方案。
思路
直接\(dfs\).
坑点在重边。有向边和无向边不能算作重边。并且最后输出边定向的时候要注意将所有的预处理时删去的重边定成和留下的那条同向。
Code
#include <bits/stdc++.h>
#define maxm 600010
#define maxn 600010
using namespace std;
typedef long long LL;
int n, m, s, tot, ne[maxn], id[maxn], ans[maxn], cnt;
bool vis[maxn];
struct node {
int u, v, t;
}a[maxm];
bool cmp(int i, int j) {
return a[i].u < a[j].u || (a[i].u==a[j].u && a[i].v<a[j].v) || (a[i].u==a[j].u && a[i].v==a[j].v && a[i].t<a[j].t);
}
struct Edge {
int to, ne, type, dir, id;
Edge(int _to=0, int _ne=0, int _type=0, int _id=0, int _dir=-1):
to(_to), ne(_ne), type(_type), dir(_dir), id(_id) {}
}edge[maxm];
void add(int type, int u, int v, int id) {
edge[tot] = Edge(v, ne[u], type, id);
ne[u] = tot++;
if (type==2) edge[tot] = Edge(u, ne[v], type, id), ne[v] = tot++;
}
void dfs1(int u) {
++cnt;
vis[u] = true;
for (int i = ne[u]; ~i; i = edge[i].ne) {
int v = edge[i].to;
if (vis[v]) continue;
edge[i].dir = 1;
dfs1(v);
}
}
void dfs2(int u) {
++cnt;
vis[u] = true;
for (int i = ne[u]; ~i; i = edge[i].ne) {
int v = edge[i].to;
if (vis[v]) continue;
if (edge[i].type == 1) dfs2(v);
else edge[i].dir = 0;
}
}
void out() {
printf("%d\n", cnt);
for (int i = 0; i < tot; ++i) {
if (edge[i].dir!=-1) ans[edge[i].id] = edge[i].to == a[edge[i].id].v ? edge[i].dir : !edge[i].dir;
}
for (int i = 1; i < m; ++i) {
if (a[id[i]].u == a[id[i-1]].u && a[id[i]].v==a[id[i-1]].v && a[id[i]].t==a[id[i-1]].t) ans[id[i]] = ans[id[i-1]];
}
for (int i = 0; i < m; ++i) {
if (a[i].t == 2) putchar(ans[i]==1 ? '+' : '-');
}
puts("");
}
int main() {
scanf("%d%d%d", &n, &m, &s);
for (int i = 0; i < m; ++i) scanf("%d%d%d", &a[i].t, &a[i].u, &a[i].v), id[i] = i;
sort(id, id+m, cmp);
memset(ne, -1, sizeof(ne));
add(a[id[0]].t, a[id[0]].u, a[id[0]].v, id[0]);
for (int i = 1; i < m; ++i) {
if (a[id[i]].u!=a[id[i-1]].u || a[id[i]].v!=a[id[i-1]].v || a[id[i]].t!=a[id[i-1]].t) add(a[id[i]].t, a[id[i]].u, a[id[i]].v, id[i]);
}
cnt = 0;
dfs1(s);
out();
cnt = 0;
for (int i = 1; i <= n; ++i) edge[i].dir = -1;
memset(vis, 0, sizeof(vis));
dfs2(s);
out();
return 0;
}
H. Palindromic Cut
题意
给定一个长度为\(n(n\leq 4e5)\)的字符串,现要将其中的字符重新安排位置,并且切成等长的若干段,使得每段都是一个回文串。问最少切成多少段。并输出一种方案。
思路
切
统计出现了奇数次的字符个数,假设为\(k\)个,则显然:
- 要分成的段数\(\geq k\)
- 每一段长度都须为奇数长
k==0
一段,不用切。
k==1
一段,不用切。
为什么?万一n为偶数呢?那不是不好放置吗?
这种情况是不可能发生的,因为 1个奇数+若干个偶数=奇数 ,即总长度必然为奇数长,直接将它放在中间即可。
其他情况
上面说到,要分成的段数\(\geq k\),每段长度为奇数,但又不仅局限于这两个条件。
注意到,如果分成的段数\(\gt k\),那么大的部分一定得是偶数。即假设分成了\(num\)段,则\(num-k\)一定得为偶数。
为什么呢?
因为将那出现奇数次的k种字符放在k段的中间位置后,多出来的num-k段,其中间的字符都是从出现了偶数次的字符中拆出来的,而要拆就得一对一对地拆,否则剩下奇数个又没法往两边分了。
于是预处理出\(n\)的所有因子,枚举分成的段数,即从\(\geq k\)的第一个开始向后枚举,\(check\)是否满足条件即可。时间复杂度\(O(\sqrt n)\).
万一不存在一种满足要求的情况呢?
不存在的。至少有一种分法满足要求,那就是全部切开。
分
输出的考虑和上面提到的拆和放的思路一致。
先是将所有出现奇数次的\(k\)种字符放到前\(k\)段的中间位置,每种放一个;
再将偶数个的一对对拆出来填到后\(n-k\)段中,这里放多少个就无所谓了,可直接对每种字符放完为止;
最后将剩下的字符从两边往中间放,满了就下一段。
注意特判分成一段的情况,直接输出即可。
Code
#include <bits/stdc++.h>
#define N 62
#define maxn 400010
char s[maxn];
int cnt[70], fac[700], n;
char ans[maxn];
using namespace std;
typedef long long LL;
int idx(char c) {
if (islower(c)) return c-'a';
else if (isupper(c)) return c-'A'+26;
else return c-'0'+52;
}
char charat(int i) {
if (i < 26) return 'a'+i;
else if (i < 52) return 'A'+i-26;
else return '0'+i-52;
}
void print1() {
puts("1");
int p = 0;
for (int i = 0; i < N; ++i) {
if (cnt[i] & 1) --cnt[i], ans[n/2] = charat(i);
while (cnt[i]) {
ans[p] = ans[n-1-p] = charat(i);
++p, cnt[i] -= 2;
}
}
ans[n] = '\0';
puts(ans);
exit(0);
}
void printn() {
printf("%d\n", n);
putchar(s[0]);
for (int i = 1; i < n; ++i) printf(" %c", s[i]); puts("");
exit(0);
}
void print(int num, int sz) {
printf("%d\n", num);
for (int i = 0; i < num-1; ++i) {
char temp = ans[i*sz+sz];
ans[i*sz+sz] = '\0';
printf("%s ", ans+i*sz);
ans[i*sz+sz] = temp;
}
printf("%s\n", ans+sz*(num-1));
}
int main() {
scanf("%d%s", &n, s);
for (int i = 0; i < n; ++i) ++cnt[idx(s[i])];
int k = 0;
for (int i = 0; i < N; ++i) if (cnt[i]&1) ++k;
if (!k || k==1) print1();
int tot = 0;
for (int i = 1; i*i <= n; ++i) {
if (i*i == n) fac[tot++] = i;
else {
if (n % i == 0) fac[tot++] = i, fac[tot++] = n/i;
}
}
sort(fac, fac+tot);
int p = lower_bound(fac, fac+tot, k) - fac;
for (; p < tot; ++p) {
if (!((fac[p]-k)&1) && ((n/fac[p])&1)) break;
}
int sz = n/fac[p], curm = 0, cur = 0;
if (sz == 1) printn();
for (int i = 0; i < N; ++i) {
if (cnt[i] & 1) ans[curm*sz+sz/2] = charat(i), ++curm, --cnt[i];
}
for (int i = 0; i < N; ++i) {
if (curm == fac[p]) break;
while (cnt[i] && curm < fac[p]) ans[curm*sz+sz/2] = charat(i), ++curm, --cnt[i];
}
int l = 0, r = sz-1;
for (int i = 0; i < N; ++i) {
if (cur == fac[p]) break;
if (l == sz/2) ++cur, l = 0, r = sz-1;
while (cnt[i]) {
if (l == sz/2) ++cur, l = 0, r = sz-1;
ans[cur*sz+l++] = ans[cur*sz+r--] = charat(i);
cnt[i] -= 2;
}
}
print(fac[p], sz);
return 0;
}
I. Photo Processing
2017-2018 ACM-ICPC, NEERC, Southern Subregional Contest I. Photo Processing
K. Road Widening
题意
给定一个数列a
,每个元素初始值为s[i]
,可以加上g[i]
的变化量,要求最终得到的数列中相邻两数的绝对值之差\(\leq 1\). 问总共最多能加的值是多少。输出总和及各点变化后的量。
思路
正着扫描一遍,限定下来一个范围。再拿这个范围的最大值去倒着扫描一遍,确定每个点的值。
Code
#include <bits/stdc++.h>
#define maxn 200010
using namespace std;
typedef long long LL;
int s[maxn], g[maxn], e[maxn], ans[maxn], st[maxn], ed[maxn];
LL res;
int main() {
int n;
scanf("%d", &n);
for (int i = 0; i < n; ++i) scanf("%d%d", &s[i], &g[i]), e[i] = s[i] + g[i];
st[0] = s[0], ed[0] = e[0];
for (int i = 1; i < n; ++i) {
st[i] = max(st[i-1]-1, s[i]);
ed[i] = min(ed[i-1]+1, e[i]);
if (st[i] > ed[i]) { puts("-1"); return 0; }
}
LL res=ed[n-1]-s[n-1];
ans[n-1] = ed[n-1];
for (int i = n-2; i >= 0; --i) {
if (ans[i+1] + 1 <= ed[i]) ans[i] = ans[i+1] + 1;
else if (ans[i+1] <= ed[i]) ans[i] = ans[i+1];
else ans[i] = ans[i+1] - 1;
res += ans[i]-s[i];
}
printf("%I64d\n%d", res, ans[0]);
for (int i = 1; i < n; ++i) printf(" %d", ans[i]); printf("\n");
return 0;
}
M. Quadcopter Competition
题意
在方格纸上走,从\((x1,y1)\)出发绕一圈回来,要求将\((x2,y2)\)包围在路径里(不能在路径上),问最小距离。
思路
就是问最小的矩形周长。
注意横或纵坐标相同的情况。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main() {
int x1, x2,y1, y2, ans;
scanf("%d%d", &x1, &y1);
scanf("%d%d", &x2, &y2);
if (y1 == y2) ans = 2 * (1+abs(x1-x2)) + 4;
else if (x1 == x2) ans = 2 * (1+abs(y1-y2)) + 4;
else {
if (x2 > x1) ++x2;
else --x2;
if (y2 > y1) ++y2;
else --y2;
ans = 2 * (abs(y1-y2) + abs(x1-x2));
}
printf("%d\n", ans);
return 0;
}
2017-2018 ACM-ICPC, NEERC, Southern Subregional Contest A E F G H I K M的更多相关文章
- 2018-2019 ICPC, NEERC, Southern Subregional Contest
目录 2018-2019 ICPC, NEERC, Southern Subregional Contest (Codeforces 1070) A.Find a Number(BFS) C.Clou ...
- Codeforces 2018-2019 ICPC, NEERC, Southern Subregional Contest
2018-2019 ICPC, NEERC, Southern Subregional Contest 闲谈: 被操哥和男神带飞的一场ACM,第一把做了这么多题,荣幸成为7题队,虽然比赛的时候频频出锅 ...
- 2018-2019 ICPC, NEERC, Southern Subregional Contest (Online Mirror) Solution
从这里开始 题目列表 瞎扯 Problem A Find a Number Problem B Berkomnadzor Problem C Cloud Computing Problem D Gar ...
- 2018.10.20 2018-2019 ICPC,NEERC,Southern Subregional Contest(Online Mirror, ACM-ICPC Rules)
i207M的“怕不是一个小时就要弃疗的flag”并没有生效,这次居然写到了最后,好评=.= 然而可能是退役前和i207M的最后一场比赛了TAT 不过打得真的好爽啊QAQ 最终结果: 看见那几个罚时没, ...
- Codeforces1070 2018-2019 ICPC, NEERC, Southern Subregional Contest (Online Mirror, ACM-ICPC Rules, Teams Preferred)总结
第一次打ACM比赛,和yyf两个人一起搞事情 感觉被两个学长队暴打的好惨啊 然后我一直做傻子题,yyf一直在切神仙题 然后放一波题解(部分) A. Find a Number LINK 题目大意 给你 ...
- codeforce1070 2018-2019 ICPC, NEERC, Southern Subregional Contest (Online Mirror, ACM-ICPC Rules, Teams Preferred) 题解
秉承ACM团队合作的思想懒,这篇blog只有部分题解,剩余的请前往星感大神Star_Feel的blog食用(表示男神汉克斯更懒不屑于写我们分别代写了下...) C. Cloud Computing 扫 ...
- 2018-2019 ICPC, NEERC, Southern Subregional Contest (Online Mirror, ACM-ICPC Rules, Teams Preferred)
A. Find a Number 找到一个树,可以被d整除,且数字和为s 记忆化搜索 static class S{ int mod,s; String str; public S(int mod, ...
- 2018-2019 ICPC, NEERC, Southern Subregional Contest (Online Mirror, ACM-ICPC Rules, Teams Preferred) Solution
A. Find a Number Solved By 2017212212083 题意:$找一个最小的n使得n % d == 0 并且 n 的每一位数字加起来之和为s$ 思路: 定义一个二元组$< ...
- 【*2000】【2018-2019 ICPC, NEERC, Southern Subregional Contest C 】Cloud Computing
[链接] 我是链接,点我呀:) [题意] [题解] 我们可以很容易知道区间的每个位置有哪些安排可以用. 显然 我们优先用那些花费的钱比较少的租用cpu方案. 但一个方案可供租用的cpu有限. 我们可以 ...
随机推荐
- Python_装饰器、迭代器、生成器
一.装饰器 装饰器的存在是为了实现开放封闭原则: 封闭: 已实现的功能代码块不应该被修改: 开放: 对现有功能的扩展开放. 理解装饰器的三要素: 函数的作用域 高阶函数 闭包 1. 闭包 闭包定义:如 ...
- 【转】Mysql查询语句优化策略
1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索 ...
- JZOJ 4737. 金色丝线将瞬间一分为二 二分答案
4737. 金色丝线将瞬间一分为二 Time Limits: 1000 ms Memory Limits: 262144 KB Detailed Limits Goto ProblemSet ...
- day09-函数讲解
1.如何定义一个函数 s = '华为加油a' def s_len(): i = 0 for k in s: i += 1 print(i) s_len() 这个函数的功能就是输出字符串的长度.但是他只 ...
- makefile学习(2)
新建目录如下: ├─include │ integrate.h │ └─src │ integrate.c │ main.c │ makefile │ └─obj obj用于存放object文件. m ...
- poj-3253 fence repair(贪心题)
题目描述: Farmer John wants to repair a small length of the fence around the pasture. He measures the fe ...
- wim
wim 编辑 WIM是英文Microsoft Windows Imaging Format(WIM)的简称,它是Windows基于文件的映像格式.WIM 映像格式并非现在相当常见的基于扇区的映像格式, ...
- centos php环境搭建
CentOS 6.5系统安装配置LAMP(Apache+PHP5+MySQL)服务器环境 准备篇: 1.配置防火墙,开启80端口.3306端口vi /etc/sysconfig/iptables-A ...
- docker安装中附带安装的其他软件
aufs-tools: Tools to manage aufs filesystems. aufs的全称是advanced multi-layered unification filesystem, ...
- server.xml属性概念
# system Processers为系统可用的线程数,一般和CPU核心线程数相同 BufferPool 是由BufferChunk组成. BufferPool的总大小为BufferChunkSiz ...