2020.9.4 解题报告
2020.9.4
答题情况
总成绩 : 140 , 排名 : 2 / 6
T1 : 100 T2 : 10 T3 : 30
各题目分析
题目 1 :
预估成绩 : 100 实际成绩 : 100 考试用时 : 8:00 ~ 10:00
找规律题,不会做,最后暴力枚举过去了。
题目 2 :
预估成绩 : 100 实际成绩 : 10 考试用时 : 10:00 ~ 11:10
想到了与标算非常相似的算法,但是对题意的理解不清,导致挂分。
题目 3 :
预估成绩 : 20 实际成绩 : 40 考试用时 : 11:10 ~ 11:30
一开始就觉得不可做,弃了。
最后没时间了,写了暴力。
题目解析
T1
找规律,考虑家庭的构造方式。
正解的构造方法是从 \(n=1\) 的情况不断在外面进行包围。
即尽量围成一个正六边形,之后把剩余的在外面围起来。
模拟即可。
考场上的构造:
找到一条最长的连续家庭,之后向上/下进行扩展,扩展的长度每次减 1。
如果可以同时扩展则扩展,否则只扩展一边。
最后构造出一个正六边形削去上下顶的样子。
需要预处理一下正六边形的大小,来找到最小的合法的 最长连续家庭的长度。
不知道正确性,但是过大样例了。
T2
考虑 \(i\) 向 \(f_i\) 连边,转化为图论问题。
仅有 \(n\) 条边,得到的图一定是一个基环内向树构成的森林。
考虑一个基环内向树。
先考虑环大小为 1 的情况,即出现自环。
可看做只有根自己连向自己,实质上是一棵普通的树。
发现从叶向上按按钮,仅有叶节点不能买到,其他的均可以买完。
对于一个树点,如果要买到它,应选择花费最小的儿子按下按钮。
答案可以 \(O(n)\) 遍历获得。
考虑断环后,套用上述做法。
对于一个节点,若其有多个儿子,且多个儿子价格不同,选择价格较高的边断掉。
若没有这样的情况,钦定环上环边 - 树边贡献最小的点断开环边。
每个点儿子中收益最大/次大的儿子,即可知道贡献之差。
T3
考虑在原串 \(S\) 中 \(O(n^2)\) 枚举答案串 \(p\),检查原串是否可以通过插入答案串得到。
发现对于某时刻插入的答案串,在插入其他串后,各字符的相对顺序不变。
考虑 DP 检查。
设 \(f_{l,r,k} = \text{true or false}\) 表示,原串的子串 \([l,r]\),取出一个子序列构成答案串的前缀 \(1\sim k\) 后,是否可以通过答案串插入构成。
根据状态,可以得到一些信息:
若 \(f_{l,r,k} = \text{true}\),子串 \([l,r]\),取出一个子序列构成答案串的前缀 \(1\sim k\) 后,长度一定是答案串的倍数。
若子串 \([l,r]\) 中存在多个不重合的子序列,为答案串的前缀,则一定不合法。
若答案串合法,则 \(f_{1,n,\operatorname{len}(p)} = \text{true}\)。
显然有两种转移:
- \(f_{l,r+1,k+1} = f_{l,r+1,k+1} \lor f_{l,r,k} (S_{r+1}=p_{k+1})\),表示在 \([l,r]\) 后接上一个字符。
- \(f_{l,r,k} = f_{l,r,k} \lor f_{l,l+t\times \operatorname{len}(p),0} (l + t\times \operatorname{len}(p) \le r)\),表示在 \([l,r]\) 后接上一个 可以通过答案串插入构成的串。
最后判断 \(f_{1,n,\operatorname{len}(p)} = \text{true}\) 是否成立。
代码实现
T1 :
考场代码
//
/*
By:Luckyblock
*/
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstring>
#define ll long long
const int kMaxn = 1e5 + 10;
//=============================================================
ll sum1[kMaxn], sum2[kMaxn];
//=============================================================
inline ll read() {
ll f = 1, w = 0;
char ch = getchar();
for (; !isdigit(ch); ch = getchar())
if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
void GetMax(int &fir_, int sec_) {
if (sec_ > fir_) fir_ = sec_;
}
void GetMin(int &fir_, int sec_) {
if (sec_ < fir_) fir_ = sec_;
}
//=============================================================
int main() {
freopen("square.in", "r", stdin);
freopen("square.out", "w", stdout);
for (int i = 1; i < kMaxn; ++ i) {
sum1[i] = sum1[i - 1] + 1ll * i;
}
for (int i = 1; i < kMaxn; ++ i) {
sum2[i] = 1ll * i * i;
}
ll n = read(), ans;
int i = 1;
while (sum2[i] < n) ++ i;
for (ll j = i; j; ++ j) {
ll nowans = 0, sum = 0;
sum = j;
ll k, cnt;
for (k = sum, cnt = 0; sum < n;) {
-- k, ++ cnt;
sum += 2ll * k;
}
nowans = 2 * (k + 1)+ 4ll * (cnt + 1ll) - 2ll;
if (n <= sum - k) nowans --;
if (i == j) ans = nowans;
if (j > i + 10 && nowans > ans) break;
if (nowans < ans) ans = nowans;
}
printf("%lld", ans);
return 0;
}
T2:
考场代码
//
/*
By:Luckyblock
*/
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <stack>
#include <queue>
#define ll long long
const int kMaxn = 2e5 + 10;
//=============================================================
struct Edge {
int u, v, ne;
} e[kMaxn << 1];
int n, m, edge_num, head[kMaxn], f[kMaxn], c[kMaxn], d[kMaxn];
int dfn_num, bel_num, dfn[kMaxn], low[kMaxn], bel[kMaxn], size[kMaxn];
ll a[kMaxn], val[kMaxn], minval[kMaxn], sumval[kMaxn];
int out[kMaxn];
std :: stack <int> st;
//=============================================================
inline int read() {
int f = 1, w = 0;
char ch = getchar();
for (; !isdigit(ch); ch = getchar())
if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
void GetMax(int &fir_, int sec_) {
if (sec_ > fir_) fir_ = sec_;
}
void GetMin(int &fir_, int sec_) {
if (sec_ < fir_) fir_ = sec_;
}
void AddEdge(int u_, int v_) {
e[++ edge_num].u = u_, e[edge_num].v = v_;
e[edge_num].ne = head[u_], head[u_] = edge_num;
}
void Tarjan(int u_) {
dfn[u_] = low[u_] = ++ dfn_num;
st.push(u_);
for (int i = head[u_]; i; i = e[i].ne) {
int v_ = e[i].v;
if (! dfn[v_]) {
Tarjan(v_);
GetMin(low[u_], low[v_]);
} else if (! bel[v_]) {
GetMin(low[u_], dfn[v_]);
}
}
if (low[u_] == dfn[u_]) {
++ bel_num;
for (int r = u_ + 1; r != u_; st.pop()) {
r = st.top();
bel[r] = bel_num;
if(val[r] < minval[bel_num]) {
minval[bel_num] = val[r];
}
sumval[bel_num] += 1ll * a[r] * val[r];
size[bel_num] ++;
}
}
}
void Topsort() {
ll ans = 0ll;
for (int i = 1; i <= bel_num; ++ i) {
if (! out[i]) {
if (size[i] == 1) continue ;
ans += 1ll * sumval[i] - 1ll * minval[i];
} else {
ans += 1ll * sumval[i];
}
}
printf("%lld", ans);
}
void koishi() {
int satori;
}
//=============================================================
int main() {
freopen("dundundun.in", "r", stdin);
freopen("dundundun.out", "w", stdout);
n = read();
memset(minval, 127, sizeof (minval));
for (int u = 1, lim = n; u <= lim; ++ u) {
f[u] = read(), c[u] = read(), d[u] = read();
a[u] = read();
if (u == f[u]) {
AddEdge(u, ++ n);
AddEdge(n, f[u]);
koishi();
a[n] = 1ll;
} else {
AddEdge(u, f[u]);
}
}
for (int i = 1; i <= n; ++ i) {
val[i] = d[f[i]] - c[i];
}
for (int i = 1; i <= n; ++ i) {
if (! dfn[i]) Tarjan(i);
}
edge_num = 0;
memset(head, 0, sizeof (head));
for (int i = 1; e[i].v; ++ i) {
int u = e[i].u, v = e[i].v;
if (bel[u] != bel[v]) {
out[bel[u]] ++;
AddEdge(bel[u], bel[v]);
}
}
Topsort();
return 0;
}
/*
7
2 1 6 1
3 1 2 3
6 1 5 2
5 1 2 2
5 1 5 10
6 1 5 2
4 1 4 3
539343565151
*/
正解
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
#define ll long long
using namespace std;
const int N=100005;
int n,idx,mn,f[N],c[N],d[N],w[N],a[N],mx[N],secmx[N],dfn[N];
long long ans;
void dfs(int x) {
if(dfn[x]==idx) {
ans-=mn;
return;
}
if(dfn[x]) return;
dfn[x]=idx;
if(mx[x]) {
ans+=1LL*w[mx[x]]*a[x];
mn=std::min(mn,w[mx[x]]-w[secmx[x]]);
if(mx[x]^x) dfs(mx[x]);
}
}
int main() {
freopen("dundundun.in","r",stdin); freopen("dundundun.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d%d%d%d",&f[i],&c[i],&d[i],&a[i]);
for(int i=1;i<=n;++i) {
w[i]=d[f[i]]-c[i];
if(w[i]<0) continue;
if(w[i]>w[mx[f[i]]]) {
secmx[f[i]]=mx[f[i]];
mx[f[i]]=i;
} else if(w[i]>w[secmx[f[i]]]) {
secmx[f[i]]=i;
}
}
for(int i=1;i<=n;++i) if(!dfn[i]) mn=(1<<30),idx++,dfs(i);
printf("%lld\n",ans);
return 0;
}
T3:
考场代码
//
/*
By:Luckyblock
*/
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <unordered_map>
#define ll long long
//=============================================================
std :: string S;
int n;
std :: unordered_map <std :: string, bool> vis;
//=============================================================
inline int read() {
int f = 1, w = 0;
char ch = getchar();
for (; !isdigit(ch); ch = getchar())
if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
void GetMax(int &fir_, int sec_) {
if (sec_ > fir_) fir_ = sec_;
}
void GetMin(int &fir_, int sec_) {
if (sec_ < fir_) fir_ = sec_;
}
bool Check(std :: string p) {
// std :: cout << p << "\n";
bool ret = true;
int n1 = p.length(), n2 = n;
std :: string tmp = S;
// std :: cout << tmp << "\n";
for (int i = 1; i <= n / n1; ++ i) {
int pos = tmp.find(p);
// printf("%d\n", pos);
if (pos == - 1) return false;
std :: string tmptmp;
for (int j = 0; j < pos; ++ j) tmptmp.push_back(tmp[j]);
for (int j = pos + n1; j < n2; ++ j) tmptmp.push_back(tmp[j]);
n2 -= n1;
tmp = tmptmp;
}
return ret;
}
//=============================================================
int main() {
freopen("string.in", "r", stdin);
freopen("string.out", "w", stdout);
int T = read();
while (T --) {
int anslth = 11451419;
std :: string ans;
std :: cin >> S;
n = S.length();
vis.clear();
for (int lth = 1; lth <= n; ++ lth) {
if (anslth < lth) break;
if (n % lth) continue ;
for (int l = 0; l < n; ++ l) {
int r = l + lth - 1;
if (r >= n) break;
std :: string tmp;
for (int i = l; i <= r; ++ i) tmp.push_back(S[i]);
if (vis.count(tmp)) continue ;
vis[tmp] = true;
if (Check(tmp)) {
if (! ans.length()) ans = tmp;
if (tmp < ans) ans = tmp;
anslth = lth;
}
}
}
std :: cout << ans << "\n";
}
return 0;
}
/*
2
ababab
aaaaabaacdacabab
*/
正解
#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;
}