天梯赛选拔赛错题分析和补题
比赛链接:https://www.luogu.com.cn/contest/314613#problems
L1-5
1.字符串的输入,如果无空格用cin,有空格用getline,如果前面有cin,后面要用getline的话,要在cin完后,加上cin.ignore()。
2.如果在循环外已经定义了a变量,那么如果在循环内再次定义该变量,就会死循环。
void solve()
{
string t;
cin >> n;
cin.ignore();
string s1 = "haha", s2 = "hehe", s3 = "[doge]";
for (int i = 1; i <= n; i++)
{
getline(cin, s);
bool ok = 0, ok2 = 0, ok3 = 1;
if (s == "")
{
cout << "Are you kidding me?" << endl;
continue;
}
for (int j = 0; j < s.size(); j++)
{
if (s[j] != ' ')
{
ok3 = 0;
break;
}
}
if (ok3)
{
cout << "Are you kidding me?" << endl;
continue;
}
int r1 = s.find(s1), r2 = s.find(s2);
if (r1 != string::npos || r2 != string::npos)
{
ok = 1;
}
int len = s.size();
int r3 = s.find(s3);
while (r3 != string::npos)
{
if (r3 + 6 == len)
{
ok2 = 1;
}
r3 = s.find(s3, r3 + 1);
}
if (ok && ok2)
{
cout << s << endl;
}
else if (ok && !ok2)
{
s = s + ' ' + s3;
cout << s << endl;
}
else
{
cout << s << endl;
}
}
}
L2-1
用树状数组,前缀和代表有多少个还在,二分查询第一个等于k的地方即为答案
// 树状数组
int tr[M];
int lb(int i)
{
return i & (-i);
}
void add(int i, int x)
{
for (; i <= n; i += lb(i))
{
tr[i] += x;
}
}
int sum(int i)
{
int ans = 0;
for (; i > 0; i -= lb(i))
{
ans += tr[i];
}
return ans;
}
int que(int l, int r)
{
return sum(r) - sum(l - 1);
}
void solve()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
add(i, 1);
}
while (m--)
{
cin >> k;
l = 1, r = n;
int res = n + 1;
while (l <= r)
{
int mid = l + ((r - l) >> 1);
if (sum(mid) >= k)
{
res = min(res, mid);
r = mid - 1;
}
else
{
l = mid + 1;
}
}
cout << res << " ";
add(res, -1);
}
cout << endl;
}
L2-2
用线性筛得出1到1e6数的不重复的质因数个数,质数p的质因数个数为1,那么i*p的质因数个数应该为1+i的质因数个数。
因为范围为1e6,2的20次方就大于1e6了,所以质因数很少,所以我们用一个数组来存,adj[i]意为有i个不同质因数的数有哪些。
那么我们可以用二分来计算,upper_bound求第一个大于r的数位置,lower_bound求第一个大于等于l的数的位置,两者相减为个数,若个数为1那么就说明i对答案有贡献,累加可得。
int cnt = 0;
int pri[M];
int vis[M];
int v[M];
void ols(int n)
{
v[1] = 0;
for (ll i = 2; i <= n; i++)
{
if (!vis[i])
{
pri[++cnt] = i;
v[i] = 1;
}
for (ll j = 1; (ll)(i * pri[j]) <= n; j++)
{
vis[i * pri[j]] = 1;
v[i * pri[j]] = 1 + v[i];
if (i % pri[j] == 0)
{
break;
}
}
}
}
vi adj[M];
void solve()
{
ols(1e6);
for (int i = 1; i <= 1e6; i++)
{
adj[v[i]].push_back(i);
}
for (int i = 0; i <= 24; i++)
{
sort(all(adj[i]));
}
int q;
cin >> q;
int cnt = 0;
while (q--)
{
cin >> l >> r;
int ans = 0;
for (int i = 0; i <= 24; i++)
{
if (adj[i].size() == 0)
continue;
int d = upper_bound(all(adj[i]), r) - lower_bound(all(adj[i]), l);
if (d == 1)
ans += i;
}
cout << ans << endl;
}
}
L3-1
我们需要求的是在能到达终点的时候,保证这条路上花费最大的那次尽量小,这句话是不是很熟悉,没错,可以使用二分,二分花费,检测mid花费能不能完成到达终点的任务,也就是最大不超过mid花费,并且保证最后到达终点的耗油量少于h就行了,在使用dij的时候,将花费大的边剪掉。
struct node
{
int to, w;
};
vector<node> adj[M];
void solve()
{
int h;
cin >> n >> m >> h;
vi a(n + 1, 0);
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
for (int i = 1; i <= m; i++)
{
int u, v, w;
cin >> u >> v >> w;
adj[u].push_back({v, w});
adj[v].push_back({u, w});
}
auto check = [&](int mid) -> bool
{
vi ans(n + 1, 1e18);
priority_queue<pii, vector<pii>, greater<>> p;
ans[1] = 0;
if (a[1] > mid)
{
return 0;
}
p.push({0, 1});
while (!p.empty())
{
auto [c, i] = p.top();
p.pop();
for (auto [to, w] : adj[i])
{
if (a[to] > mid)
{
continue;
}
if (ans[to] > w + c)
{
ans[to] = w + c;
p.push({w + c, to});
}
}
}
if (ans[n] <= h)
{
return 1;
}
else
return 0;
};
l = 0, r = 2e9;
int res = r;
while (l <= r)
{
int mid = l + ((r - l) >> 1);
if (check(mid))
{
res = mid;
r = mid - 1;
}
else
{
l = mid + 1;
}
}
if (res == 2e9)
{
cout << "MenWaiShuZhe" << endl;
}
else
{
cout << res << endl;
}
}
L3-2
提取一下关键信息,题目告诉了我同一世界不能开传送门,而且有距离的限制,这个限制就是说两个相同世界的国家不可以有相同的邻居,这就是二分图匹配,什么意思呢,就是说有两堆点,堆内部不可以连边,只可以与另外一堆连边,而且一个点只能连一个点。
如何计算匹配的结果?可以枚举连的边数,最小是0,最大是min(n,m),然后每一个边数得到的结果累加就是匹配的结果。
那如何计算固定边数下两堆连的方案数呢?C(n,i)C(m,i)(i!),意思就是从两堆中各挑出i个点,第一个点能配i个点,第二个可以配(i-1)个,以此类推就是(i!),所以这个解释就很明了了。
注意!,刚刚只是计算了两堆点的方案数,我们易得出,一共有三堆,(a,b),(b,c),(a,c),而且这三堆的计算是独立的,所以采用乘法原理相乘就行了。
int fac[M];
int ifac[M];
void init()
{
fac[0] = 1;
for (int i = 1; i <= 5000; i++)
{
fac[i] = fac[i - 1] * i % mod;
}
ifac[5000] = ksm(fac[5000], mod - 2, mod);
for (int i = 4999; i >= 0; i--)
{
ifac[i] = ifac[i + 1] * (i + 1) % mod;
}
}
int ci(int n, int k)
{
return fac[n] * ifac[n - k] % mod * ifac[k] % mod;
}
int a, b, c;
int f(int x, int y)
{
int ans = 0;
for (int i = 0; i <= min(x, y); i++)
{
ans = (ans + ci(x, i) * ci(y, i) % mod * fac[i]) % mod;
}
return ans;
}
void solve()
{
init();
cin >> a >> b >> c;
cout << f(a, b) * f(b, c) % mod * f(a, c) % mod << endl;
}
L2-3
一看是个环,然后很容易就知道不是贪心而是dp,所以是一道很容易看出来的区间dp,dp[i][l],表示从i开始l长度所能获得的能量,那么只有两个变量一个是起点一个是长度,所以状态转移方程就是dp[i][l]=max(dp[i+1][l-1]+ia[i],dp[i][l-1]+ia[i+l-1]),前者表示取左端点,后者表示取右端点,那么只需找出最大的dp[i][n]就是结果,最小的起始点也易得。
void solve()
{
int h = 0;
cin >> h >> n;
vi a(n + 1);
memset(d2, 0, sizeof(d2));
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
vi b(2 * n + 1);
for (int i = 1; i <= 2 * n; i++)
{
if (i <= n)
b[i] = a[i];
else
b[i] = a[i - n];
d2[i][1] = b[i] * n;
}
for (int i = 2; i <= n; i++)
{
int st = n - i + 1;
for (int l = 1; l + i - 1 <= 2 * n; l++)
{
d2[l][i] = max(d2[l + 1][i - 1] + st * b[l], d2[l][i - 1] + st * b[l + i - 1]);
}
}
int mx = INT_MIN;
int mi = INT_MAX;
for (int k = 1; k <= n; k++)
{
int now = b[k] + d2[k + 1][n - 1];
if (now > mx)
{
mx = now;
mi = k;
}
else if (now == mx)
{
mi = min(mi, k);
}
}
cout << mx;
if (h)
cout << ' ' << mi << endl;
else
cout << endl;
}
L3-3
这个题就是tarjan强连通分量求scc缩点的板子,tarjan可以将有向有环图转换成有向无环图,然后在新的无环图上进行拓扑,这样就可以求得每个缩点可以获取的最大利润,再拿着最大利润去找最大的siz,就可以得到结果。
vi adj[M];
vi ad2[M];
int w[M];
int dfn[M], low[M], tim = 0;
int stk[M], vis[M], pos = 0;
int scc[M], siz[M], cnt = 0;
int dp[M], si[M];
void tarjan(int x)
{
tim++;
dfn[x] = tim, low[x] = tim;
pos++;
stk[pos] = x, vis[x] = 1;
for (int i : adj[x])
{
if (!dfn[i])
{
tarjan(i);
low[x] = min(low[x], low[i]);
}
else if (vis[i])
{
low[x] = min(low[x], dfn[i]);
}
}
if (dfn[x] == low[x])
{
int te;
cnt++;
do
{
te = stk[pos--];
vis[te] = 0;
scc[te] = cnt;
siz[cnt]++;
} while (te != x);
}
}
void solve()
{
cin >> n >> m;
vi a(n + 1);
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
for (int i = 1; i <= m; i++)
{
int u, v;
cin >> u >> v;
adj[u].push_back(v);
}
for (int i = 1; i <= n; i++)
{
if (!dfn[i])
tarjan(i);
}
vi ind(cnt + 1, 0);
for (int i = 1; i <= n; i++)
{
w[scc[i]] += a[i];
for (int j : adj[i])
{
int ha = scc[i];
int hb = scc[j];
if (ha != hb)
{
ad2[scc[i]].push_back(scc[j]);
ind[hb]++;
}
}
}
queue<int> p;
for (int i = 1; i <= cnt; i++)
{
si[i] = siz[i];
dp[i] = w[i];
if (ind[i] == 0)
{
p.push(i);
}
}
while (p.size())
{
auto i = p.front();
p.pop();
for (int j : ad2[i])
{
ind[j]--;
if (ind[j] == 0)
{
p.push(j);
}
if (dp[i] + w[j] > dp[j])
{
dp[j] = max(dp[j], dp[i] + w[j]);
si[j] = si[i] + siz[j];
}
else if (dp[i] + w[j] == dp[j])
{
si[j] = max(si[j], si[i] + siz[j]);
}
}
}
int ans = 0;
int mx = 0;
for (int i = 1; i <= cnt; i++)
{
if (ans < dp[i])
{
ans = max(ans, dp[i]);
mx = si[i];
}
else if (ans == dp[i])
{
mx = max(mx, si[i]);
}
}
cout << ans << ' ' << mx << endl;
}

浙公网安备 33010602011771号