// 深夜补水题,清早(雾)写水文

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

题意

用拉丁字母书写波兰姓名可能会有歧义,体现在两点上:

  1. \(kh\leftrightarrow h\)
  2. \(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\)个,则显然:

  1. 要分成的段数\(\geq k\)
  2. 每一段长度都须为奇数长

k==0

一段,不用切。

k==1

一段,不用切。

为什么?万一n为偶数呢?那不是不好放置吗?
这种情况是不可能发生的,因为 1个奇数+若干个偶数=奇数 ,即总长度必然为奇数长,直接将它放在中间即可。

其他情况

上面说到,要分成的段数\(\geq k\),每段长度为奇数,但又不仅局限于这两个条件

注意到,如果分成的段数\(\gt k\),那么大的部分一定得是偶数。即假设分成了\(num\)段,则\(num-k\)一定得为偶数。

为什么呢?
因为将那出现奇数次的k种字符放在k段的中间位置后,多出来的num-k段,其中间的字符都是从出现了偶数次的字符中拆出来的,而要拆就得一对一对地拆,否则剩下奇数个又没法往两边分了。

于是预处理出\(n\)的所有因子,枚举分成的段数,即从\(\geq k\)的第一个开始向后枚举,\(check\)是否满足条件即可。时间复杂度\(O(\sqrt n)\).

万一不存在一种满足要求的情况呢?
不存在的。至少有一种分法满足要求,那就是全部切开。

输出的考虑和上面提到的拆和放的思路一致。

  1. 先是将所有出现奇数次的\(k\)种字符放到前\(k\)段的中间位置,每种放一个;

  2. 再将偶数个的一对对拆出来填到后\(n-k\)段中,这里放多少个就无所谓了,可直接对每种字符放完为止;

  3. 最后将剩下的字符从两边往中间放,满了就下一段。

注意特判分成一段的情况,直接输出即可。

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的更多相关文章

  1. 2018-2019 ICPC, NEERC, Southern Subregional Contest

    目录 2018-2019 ICPC, NEERC, Southern Subregional Contest (Codeforces 1070) A.Find a Number(BFS) C.Clou ...

  2. Codeforces 2018-2019 ICPC, NEERC, Southern Subregional Contest

    2018-2019 ICPC, NEERC, Southern Subregional Contest 闲谈: 被操哥和男神带飞的一场ACM,第一把做了这么多题,荣幸成为7题队,虽然比赛的时候频频出锅 ...

  3. 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 ...

  4. 2018.10.20 2018-2019 ICPC,NEERC,Southern Subregional Contest(Online Mirror, ACM-ICPC Rules)

    i207M的“怕不是一个小时就要弃疗的flag”并没有生效,这次居然写到了最后,好评=.= 然而可能是退役前和i207M的最后一场比赛了TAT 不过打得真的好爽啊QAQ 最终结果: 看见那几个罚时没, ...

  5. Codeforces1070 2018-2019 ICPC, NEERC, Southern Subregional Contest (Online Mirror, ACM-ICPC Rules, Teams Preferred)总结

    第一次打ACM比赛,和yyf两个人一起搞事情 感觉被两个学长队暴打的好惨啊 然后我一直做傻子题,yyf一直在切神仙题 然后放一波题解(部分) A. Find a Number LINK 题目大意 给你 ...

  6. codeforce1070 2018-2019 ICPC, NEERC, Southern Subregional Contest (Online Mirror, ACM-ICPC Rules, Teams Preferred) 题解

    秉承ACM团队合作的思想懒,这篇blog只有部分题解,剩余的请前往星感大神Star_Feel的blog食用(表示男神汉克斯更懒不屑于写我们分别代写了下...) C. Cloud Computing 扫 ...

  7. 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, ...

  8. 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$ 思路: 定义一个二元组$< ...

  9. 【*2000】【2018-2019 ICPC, NEERC, Southern Subregional Contest C 】Cloud Computing

    [链接] 我是链接,点我呀:) [题意] [题解] 我们可以很容易知道区间的每个位置有哪些安排可以用. 显然 我们优先用那些花费的钱比较少的租用cpu方案. 但一个方案可供租用的cpu有限. 我们可以 ...

随机推荐

  1. linux下vim命令

    进入vim的命令 vim filename :打开或新建文件,并将光标置于第一行首 vim +n filename :打开文件,并将光标置于第n行首 vim + filename :打开文件,并将光标 ...

  2. Green Space【绿色空间】

    Green Space Living in an urban area with green spaces has a long-lasting positive impact on people's ...

  3. Neon Lights in Hong Kong【香港霓虹灯】

    Neon Lights in Hong Kong Neon is to Hong Kong as red phone booths are to London and fog is to San Fr ...

  4. TS各个表 与 SECTION 的解析 CAS原理

    TS流,通过一个个的TS包来传送: TS包可以是传送PSI SI等各表的数据包,也可以是传送节目音视频数据(携带的PES包:音视频基本流包)的包:TS携带 PSI SI等表的数据时,各个表以各表对应的 ...

  5. utf8和utf8mb4区别

    原文链接 一.简介 MySQL在5.5.3之后增加了这个utf8mb4的编码,mb4就是most bytes 4的意思,专门用来兼容四字节的unicode.好在utf8mb4是utf8的超集,除了将编 ...

  6. The DOM in JavaScript

    DOM : Document Object Model   D is for document :  The DOM cant work without a document . When you c ...

  7. POJ1741 Tree (点分治)

    Tree Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 25772   Accepted: 8566 Description ...

  8. C语言其他知识总结

    1.常指针与指针常量 诀窍:观察const修饰的是谁,那谁就不可更改. const int *p;//const 修饰*p,所以*p不能更改.通过指针不可更改指向的值 int const *p;//同 ...

  9. IOS开发学习笔记006 - 指针

    C语言 指针 简单使用如下: int *p;//定义 int a = 10; p = &a;//赋值 int * b = &a;//定义并赋值 定义格式:类型 *指针变量名: 注意事项 ...

  10. mysql的下载及配置(复制1)

    ---恢复内容开始--- MySQL数据库安装与配置详解 目录 一.概述 二.MySQL安装 三.安装成功验证 四.NavicatforMySQL下载及使用 一.概述 MySQL版本:5.7.17 下 ...