2020.09.04考试解题报告
总结
预计得分:\(50+10+0\)
实际得分:\(40+10+0\)
\(T1\) 显然是个找规律题,我想到了要先摆成最大的正六边形,然后再围起来,但是不知道围的时候该怎么围,于是就只写了 \(50\) 分的做法,不过因为数数不好又丢分了。
\(T2\) 大家都在想图,我啥也没想出来,就一直模拟、贪心,死活过不了样例,所以就只写了 \(f_i=i\) 的 \(10\) 分
\(T3\) 最后半个小时想用 \(string\) 函数艹一波来着,但是不知道为什么报错了
如果再细心一点过一百是没问题的……(也就这个数了)
思路
T1 方
前 \(40\%\),即 \(N\le 20\) 的数据可以直接手算。
当 \(N=6\times\dfrac{k(k+1)}{2}+1(k\in\N)\) 时,发现刚好围成六边形,输出六边形外侧的周长即可。
考虑将小方格摆成能摆成的最大的正六边形,然后处理剩下的部分,将其摆在外侧。
如果六边形有 \(x\) 层,那么下一层的数的个数就有 \(6\times x\) 个。手推发现接下来 \(x-1\) 个数的答案相同,也就是正六边形向外扩展的六条边中的第一条边。
接下来 \(x\)、\(x\)、\(x\)、\(x\) 个数的答案也是各自相同的,分别对应新的四条边。
再接下来扩展的外层还剩 \(x+1\) 个数,这 \(x+1\) 个数的答案也是相同的。
所以特判六次,如果当前数小于 \(n\),那么就每次增加如上所说的数。
T2
此题的建模是一个基环内向树(或者森林)。
先咕了。
大概就是找出每个点能够获得的最大收益,优先使用这个收益,并将获得最大收益的点向自己连边。
顺便记录一下次大收益,最后在环上肯定有取不到的点,将收益减去最大收益和次大收益的差值即可。
特判出现自环的情况,如果 \(mx_x!=x\) 则可以继续搜索。
T3
正解:
考虑一个字符串,它的所有元素会在后面的操作中逐渐分离,但是它们的相对顺序是不变的。
而且它原来相邻的两个元素在后面的操作之后,两个元素之间的空间可以转化成个子问题。如果可行,则这个子问题也必定可行。
先考虑普通的 \(\text{DP}\) 思路: \(f_{l,r,k}\) 表示区间 \([l,r]\) 中, 有零散的 \(k\) 个字母拼起来等于\(p_{1\sim k}\),其它的都合法,是否可行。
转移有两种: 一种是从\(f_{l,r - 1,k- 1}\) 转移过来,条件是 \(s[j] = p[k]\),就是加上一个零散的元素。
另一种是从\(f_{l,j,k}\ and\ f_{j,r,0}\) 就是在后面拼一个合法的。
然而这样会 \(\text{TLE}\)。
紧接着我们发现,实际上,\(k=(r-l+ 1) mod len\)
所以就可以省去一维,然后就可以 \(\text{AC}\) 了。
代码
T1
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define int long long
using namespace std;
const int A = 1e5 + 11;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
inline int read() {
char c = getchar();
int x = 0, f = 1;
for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
return x * f;
}
int n;
int a[22] = {0, 6, 8, 9, 10, 11, 12, 12, 13, 14, 14};
signed main() {
n = read();
if (n <= 7) return cout << a[n] << '\n', 0;
n--;
int now = 1;
while ((now + 1) * (now + 2) * 3 <= n) now++;
if (now * (now + 1) * 3 == n) return cout << (now + 1) * 6 << '\n', 0;
int ans = (now + 1) * 6;
int cnt = now * (now + 1) * 3 + 1;
n++, now++;
//先判再加
if (cnt < n) ans++, cnt += now - 1;
if (cnt < n) ans++, cnt += now;
if (cnt < n) ans++, cnt += now;
if (cnt < n) ans++, cnt += now;
if (cnt < n) ans++, cnt += now;
if (cnt < n) ans++, cnt += now + 1;
cout << ans << '\n', 0;
return 0;
}
T2
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int A = 1e5 + 11;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
inline int read() {
char c = getchar();
int x = 0, f = 1;
for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
return x * f;
}
long long ans;
int n, cnt, tot, mn;
struct node { int to, nxt; } e[A];
int mx[A], smx[A], val[A], vis[A];
int f[A], c[A], d[A], a[A], head[A];
inline void add(int from, int to) {
e[++tot].to = to;
e[tot].nxt = head[from];
head[from] = cnt;
}
void dfs(int x) {
if (vis[x] == cnt) return ans -= mn, void();
if (vis[x]) return;
vis[x] = cnt, mn = min(mn, val[mx[x]] - val[smx[x]]);
if (mx[x] != x) dfs(mx[x]);
}
int main() {
n = read();
for (int i = 1; i <= n; i++)
f[i] = read(), c[i] = read(), d[i] = read(), a[i] = read();
for (int i = 1; i <= n; i++) {
val[i] = d[f[i]] - c[i];
if (val[i] < 0) continue;
if (val[i] > val[mx[f[i]]]) smx[f[i]] = mx[f[i]], mx[f[i]] = i;
//待确认是>还是>=
else if (val[i] > val[smx[f[i]]]) smx[f[i]] = i;
}
for (int i = 1; i <= n; i++) ans += 1ll * a[i] * val[mx[i]];
for (int i = 1; i <= n; i++) if (mx[i]) add(mx[i], i);
for (int i = 1; i <= n; i++) if (!vis[i]) ++cnt, mn = inf, dfs(i);
cout << ans << '\n';
return 0;
}
T3
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
#define ll long long
#define M
using namespace std;
int read() {
int nm = 0, f = 1;
char c = getchar();
for(; !isdigit(c); c = getchar()) if(c == '-') f = -1;
for(; isdigit(c); c = getchar()) nm = nm * 10 + c - '0';
return nm * f;
}
char s[301];
char s2[301];
char s4[301];
int note[33];
int note2[33];
bool f = false;
bool dp[220][220];
bool check(int len1, int len2) {
for(int i = 1; i <= len1; i++) dp[i][i - 1] = true;
for(int len = 1; len <= len1; len++) {
for(int i = 1; i + len - 1 <= len1; i++) {
int l = i;
int r = i + len - 1;
dp[l][r] = false;
int op = (len - 1) % len2 + 1;
if(dp[l][r - 1] && s2[op] == s[r - 1]) dp[l][r] = true;
else
for(int k = 1; k * len2 <= len; k++) {
if(dp[l][r - k * len2] && dp[r - k * len2 + 1][r]) dp[l][r] = true;
}
}
}
return dp[1][len1];
}
int main() {
freopen("string.in","r",stdin);
freopen("string.out","w",stdout);
int t = read();
while(t--) {
scanf("%s", s);
memset(s2, 0, sizeof(s2));
memset(s4, 0, sizeof(s4));
memset(note, 0, sizeof(note));
int len = strlen(s);
for(int i = 0; i < len; i++) note[s[i] - 'a']++;
f = false;
for(int j = 1; j <= len / 2; j++) {
if(f) break;
if(len % j != 0) continue;
for(int i = 0; i + j - 1 < len; i++) {
int l = i, r = i + j - 1;
for(int k = l; k <= r; k++) note2[s[k] - 'a']++;
int op = 0, flag = 1;
for(int k = 0; k <= 25; k++) {
if(!flag) break;
if(note[k] != 0 && note2[k] == 0) {
flag = false;
break;
}
if(note[k] == 0 && note2[k] == 0) continue;
if(op == 0) op = note[k] / note2[k];
else if(note[k] / note2[k] != op) flag = 0;
}
for(int k = l; k <= r; k++) note2[s[k] - 'a']--;
if(flag) {
for(int k = l; k <= r; k++) s2[k - l + 1] = s[k];
if(check(len, j)) {
f = true;
bool potato = false;
for(int k = l; k <= r; k++) s4[k - l] = s[k];
break;
}
}
}
}
if(!f) printf("%s\n", s);
else {
for(int i = 0; s4[i] >= 'a' && s4[i] <= 'z'; i++) putchar(s4[i]);
cout << "\n";
}
}
return 0;
}