第三周训练总结
第三周训练总结
比赛
个人排位赛7
- F:简单题,将mod k相同位置的数字排好序放回去,最后判断是否是上升序列即可
- I;水题,求两个点的曼哈顿距离
- j:模拟,一开始直接用set模拟T了,改用map可以过;但是结束之后我尝试了各种优化,最后用set过了:
- 首先不能用count函数,这个count函数是画蛇添足的,我们去找一个元素c遍,如果找不到就直接break
- 其次不能使用迭代器遍历每一个元素,可以选择使用lower_bound进行二分查找,复杂度到 \(O(log n)\)
个人排位赛8
- A:模拟,首先假设只有一个篮子,算出要装几天,然后除以2向上取整;wa了三发是因为double好像会丢精度
- B:等比数列,特判q=-1的情况,其他的直接模拟
- F:水题
- G:并查集,判断每个集合中的边数量和点数量是否符合要求即可
个人排位赛9
- A:水题,直接将同一编号的两个车取消一辆即可
- B:差分,每次讲最后一串数加一或者减一,因此枚举左端点即可,比较容易算出如果不在一开始替换数字需要多少代价才能完成;然后考虑替换哪个数字对答案贡献最大
if( i + 2 <= n ){
int tt = abs(a[i + 1] - a[i + 2]) + abs(a[i]-a[i + 1]) - abs(a[i] - a[i + 2]);
kl = max(kl, tt);
}
- G:模拟,简单题,判断替换代价和带来的收益即可
- H:思维,每次用最大的 \(n/2\) 的配上最小的
- I:思维,可以考虑换三次必定换成功一个且仅有一个数字,直接暴力模拟即可
组队训练赛3
共AC六题,本人AC3题,其中H题为队友提供思路
- A:水题,直接模拟算日期
- C:水题,看着挺唬人,实际只有三行代码:
m -= (n - 1);
int f = m % k;
cout << 3 - (s[f] - '0') << endl;
比较恶心的是输入输出T了一发
- H:将左端点排序,每次记录目前最大的左端点是多少计为maxx,如果当前遍历到的左端点比maxx更小则将左端点右移一格,等待继续遍历,使用优先队列维护
本场组队赛较为简单,但是只打了2h30min,如果后面打满不知道能不能再a一个
个人训练
本周确定了队伍分工,主打图论,因此整理了一下模板,复习了一些算法:
Tarjan:
// luogu P3388 【模板】割点(割顶)
#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 2e4+10;
int n, m, t, root;
int ans = 0;
int head[maxn], ct;
struct Edge {
int val, to, nxt;
}edge[maxn*10];
inline void addedge(int s, int t) {
edge[++ct].to = t;
edge[ct].nxt = head[s];
head[s] = ct;
}
inline void init() {
memset(head, -1, sizeof head);
ct = 0;
}
int dfn[maxn], low[maxn], dfncnt, st[maxn], in_stack[maxn], tp;
int scc[maxn], sc; // 结点 i 所在 SCC 的编号
int sz[maxn],ind[maxn],od[maxn]; // 强连通 i 的大小
int is_cut[maxn];
void tarjan(int u) {
low[u] = dfn[u] = ++dfncnt;
int cnt = 0;
for (int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].to;
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
if (low[v] >= dfn[u]) {
cnt++;
if (u != root || cnt > 1) {
is_cut[u] = 1;
}
}
}
else low[u] = min(low[u], dfn[v]);
}
}
inline void solve() {
cin >> n >> m;
for (int i = 1; i <= m; ++i) {
int s, t;
cin >> s >> t;
if (s == t)continue;
addedge(s, t);
addedge(t, s);
}
for (int i = 1; i <= n; ++i) {
if (dfn[i] == 0) {
root = i;
tarjan(i);
}
}
for (int i = 1; i <= n; ++i) {
if (is_cut[i])ans++;
}
cout << ans << endl;
for (int i = 1; i <= n; ++i) {
if (is_cut[i])cout << i << " ";
}
return;
}
int main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int T;
T = 1;
while (T--) {
init();
solve();
}
return 0;
}
缩点:
int dfn[maxn], low[maxn], dfncnt, st[maxn], in_stack[maxn], tp;
int scc[maxn], sc; // 结点 i 所在 SCC 的编号
int sz[maxn]; // 强连通 i 的大小
void tarjan(int u) {
low[u] = dfn[u] = ++dfncnt, st[++tp] = u, in_stack[u] = 1;
for (int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].to;
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
} else if (in_stack[v]) {
low[u] = min(low[u], dfn[v]);
}
}
if (dfn[u] == low[u]) {
++sc;
while (st[tp] != u) {
scc[st[tp]] = sc;
sz[sc]++;
in_stack[st[tp]] = 0;
--tp;
}
scc[st[tp]] = sc;
sz[sc]++;
in_stack[st[tp]] = 0;
--tp;
}
}
以及网络流和二分图匹配:
// 注意建图操作cnt从1开始!!!建图的同时建边权为零的反图
int dep[maxn],no[maxn];// 弧优化
inline bool bfs(){
for(int i=1;i<=n;++i)dep[i]=INF;
dep[s]=0;
queue<int> q;
q.push(s);
no[s]=head[s];
while(!q.empty()){
int now=q.front();
q.pop();
for(int i=head[now];~i;i=edge[i].nxt){
int to=edge[i].to;
if(edge[i].val>0&&dep[to]==INF){
q.push(to);
no[to]=head[to];
dep[to]=dep[now]+1;
if(to==t)return true;
}
}
}
return false;
}
inline int dinic(int x,ll flow){
if(x==t)return flow;
ll tmp,sum=0;
for(int i=no[x];(~i)&&flow;i=edge[i].nxt){
no[x]=i;
int to=edge[i].to;
if(edge[i].val>0&&(dep[to]==dep[x]+1)){
tmp=dinic(to,min(flow,edge[i].val));
if(tmp==0)dep[to]=INF; //剪枝
flow-=tmp;
sum+=tmp;
edge[i].val-=tmp;
edge[i^1].val+=tmp;
}
}
// if(sum==0)return dep[x]=0;
return sum;
}
其中写dinic的题时候因为cnt的值wa了十多发,因此在模板中重点标注了