0%

220906 总结

时间有点短,做到后面稍微有点慌

怎么就想不到应该用什么算法呢。。。

赛时估计:$100+30+0$

赛后测试:$100+30+0$

赛后一看,还真没难题

T1 购物

直接模拟即可

T2 第 k 小

想的是将所有区间排序,顺着区间统计数量,该区间结束就在下一个区间的这个位置接着跑

但是值域 $2\times10^9$ 跑不过啊 /youl

做的时候一直在想怎么缩小值域去统计结果(

最后打了个暴力就跑路了

应该但没意识到这个答案具有二分性质,即小于第 $k$ 小的数都比第 $k$ 小的数小(什么废话

但是一看题解就发现了。。。呜

令 $x$ 表示第 $k$ 小的数,二分 $x$

check 的时候扫一遍所有区间统计小于等于 $x$ 的数的个数 $s$

如果 $s \ge K$ 则表示 $x$ 取大了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include<bits/stdc++.h>
using namespace std;

const int maxN = 1e5;

struct Segment {
int l, r;
friend bool operator < (Segment A, Segment B) {
if (A.l != B.l) return A.l < B.l;
return A.r > B.r;
}
} A[maxN];

int N, K;

bool check(int k) {
int cnt = 0;
for (int i = 1; i <= N; i++) {
if (A[i].l <= k) cnt += min(k, A[i].r) - A[i].l + 1;
}
return cnt >= K;
}

int main() {
freopen("thekth.in", "r", stdin);
freopen("thekth.out", "w", stdout);

scanf("%d", &N);
for (int i = 1; i <= N; i++) {
int l, r;
scanf("%d%d", &A[i].l, &A[i].r);
}

scanf("%d", &K);

int l = -1e9;
int r = 1e9;
int ans = 0;
while (l <= r) {
int mid = (l + r) >> 1;
if (check(mid)) {
ans = mid;
r = mid - 1;
} else l = mid + 1;
}

printf("%d", ans);
return 0;
}

T3 最大半连通子图

考的时候在要不要略过 T2 之间摇摆不定导致只剩半小时写这道题了…寄!

半小时糊了一个实际上思路正确但被我写挂了的TLE做法

将所有的 scc 缩成一个点,那么最大半联通子图实际上就是找缩完点形成的这张 DAG 上的最长链

使用拓扑排序更新最长链的长度,如果成功更新,那么重置长度最大值及个数;如果当前长度与最长链长度相同,则更新最长链个数。

Lg 上这么就能过了,但是 Lemon 测的时候还得卡卡常才能过(

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include<bits/stdc++.h>
using namespace std;

const int maxN = 1e5 + 10;
int MOD;

vector<int> G[maxN];

int N, M;

stack<int>s;
int ins[maxN];
int dfn[maxN];
int low[maxN];

int cnt = 0;
int sccNum = 0;
int sccSiz[maxN];
int scc[maxN];

void tarjan(int u) {
cnt++;
dfn[u] = cnt;
low[u] = cnt;
s.push(u);
ins[u] = 1;
for (auto i : G[u]) {
int v = i;
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
} else if (ins[v]) {
low[u] = min(low[u], dfn[v]);
}
}
if (low[u] == dfn[u]) {
ins[u] = 0;
sccNum++;
scc[u] = sccNum;
sccSiz[sccNum] = 1;
while (s.top() != u) {
int sT = s.top();
scc[sT] = sccNum;
ins[sT] = 0;
sccSiz[sccNum]++;
s.pop();
}
s.pop();
}
return;
}

vector<int> G2[maxN];
int inD[maxN];

inline void reBuid() {
unordered_set<long long> S;

for (int i = 1; i <= N; i++) {
for (auto j : G[i]) {
long long H = 100000ll * scc[i] + scc[j];
if (S.count(H)) continue;
if (scc[i] != scc[j]) {
G2[scc[i]].push_back(scc[j]);
inD[scc[j]]++;
}
S.insert(H);
}
}
}

int dp[maxN];
int num[maxN];

inline void Topo() {
queue<int> Q;

for (int i = 1 ; i <= sccNum; i++) {
if (inD[i] == 0) Q.push(i);
dp[i] = sccSiz[i];
num[i] = 1;
}

while (!Q.empty()) {
int u = Q.front();
Q.pop();
for (auto i : G2[u]) {
if (dp[i] < dp[u] + sccSiz[i]) {
dp[i] = dp[u] + sccSiz[i];
num[i] = num[u];
} else if (dp[i] == dp[u] + sccSiz[i]) {
num[i] = (num[u] % MOD + num[i] % MOD) % MOD;
}
if (--inD[i] == 0) Q.push(i);
}
}
}

int main() {
ios :: sync_with_stdio(false);

freopen("semi.in", "r", stdin);
freopen("semi.out", "w", stdout);

cin >> N >> M >> MOD;

for (int i = 1; i <= M; i++) {
int u, v;
cin >> u >> v;
G[u].push_back(v);
}
for (int i = 1; i <= N; i++) if (!dfn[i]) tarjan(i);

reBuid();
Topo();

int dis = -1;
int ans = -1;

for (int i = 1; i <= sccNum; i++) {
if (dp[i] > dis) {
dis = dp[i];
ans = num[i];
} else if (dp[i] == dis) {
ans = (ans % MOD + num[i] % MOD) % MOD;
}
}
printf("%d\n%d", dis, ans);
return 0;
}

简单总结

  • 稳一点

  • 觉得算法不行的时候考虑考虑题目具有的性质

  • 别急,急也没用.jpg

  • 码力不太够,表现在代码细节出现令人智熄的离谱错误