[赛记] csp-s模拟7
median 50pts
错解50pts(有重复的数就不行);
赛时想容斥了,其实不用容斥(好像也不能容斥);
题解做法:将每个数存一个二元组,按大小排序,枚举每一个数作为中位数,再枚举每个位置的种类,看它前面和后面有多少这些种类的数,乘起来即可;
这样就巧妙地避免了重复的情况,如果直接枚举,则有相同的数会被重复算,而这个就直接乘 $ 0 $ 了,非常巧妙;
令 $ k $ 为种类数($ k = 5 $),那么时间复杂度为: $ O(n \times k^6) $,实际情况根本跑不满,差不多 $ k^3 $ 左右;
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#define int long long
const long long mod = 998244353;
int n;
int a[8][100005], qsum[500005][8], hsum[500005][8];
pair<int, int> b[1000005];
int cnt;
long long ans;
void w(int a, int bb, int c, int d, int e) {
for (int i = 1; i <= cnt; i++) {
if (b[i].second != c) continue;
int o = qsum[i][a] * qsum[i][bb] % mod * hsum[i][d] % mod * hsum[i][e] % mod;
ans = (ans + o * b[i].first % mod) % mod;
}
}
signed main() {
freopen("median.in", "r", stdin);
freopen("median.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int j = 1; j <= 5; j++) {
for (int i = 1; i <= n; i++) {
cin >> a[j][i];
b[++cnt] = {a[j][i], j};
}
}
sort(b + 1, b + 1 + cnt);
for (int i = 1; i <= cnt; i++) {
for (int j = 1; j <= 5; j++) {
qsum[i + 1][j] = qsum[i][j];
}
qsum[i + 1][b[i].second]++;
}
for (int i = cnt; i >= 1; i--) {
for (int j = 1; j <= 5; j++) {
hsum[i - 1][j] = hsum[i][j];
}
hsum[i - 1][b[i].second]++;
}
for (int p3 = 1; p3 <= 5; p3++) {
for (int p1 = 1; p1 <= 5; p1++) {
if (p1 == p3) continue;
for (int p2 = p1 + 1; p2 <= 5; p2++) {
if (p2 == p3) continue;
for (int p4 = 1; p4 <= 5; p4++) {
if (p4 == p3 || p4 == p2 || p4 == p1) continue;
for (int p5 = p4 + 1; p5 <= 5; p5++) {
if (p5 == p1 || p5 == p2 || p5 == p3) continue;
w(p1, p2, p3, p4, p5);
}
}
}
}
}
cout << ans;
return 0;
}
travel 0pts
赛时全输出$ Yes $ 搞了90pts,赛后绑了包0pts;
考虑题意转化,其实就是在让我们判断一个图是否有二元及以上的环或者有两个及以上的自环在同一路径上;
那么我们先用 $ Tarjan $ 缩点看一下缩点以后的点数是否等于原点数以此判断第一个,然后跑一边深搜找一下第二个即可;
时间复杂度:$ O(n + m) $;
貌似赛后数据暴搜也能过?
点击查看代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
using namespace std;
int n, m;
struct sss{
int t, ne;
}e[2000005];
int h[2000005], cnt;
void add(int u, int v) {
e[++cnt].t = v;
e[cnt].ne = h[u];
h[u] = cnt;
}
bool vis[500005], v[500005];
int vi[500005];
int dfn[500005], low[500005], dcnt;
stack<int> s;
int sum;
void Tarjan(int x) {
dfn[x] = low[x] = ++dcnt;
s.push(x);
v[x] = true;
for (int i = h[x]; i; i = e[i].ne) {
int u = e[i].t;
if (!dfn[u]) {
Tarjan(u);
low[x] = min(low[x], low[u]);
} else if (v[u]) {
low[x] = min(low[x], dfn[u]);
}
}
if (dfn[x] == low[x]) {
sum++;
int t = 0;
do {
t = s.top();
s.pop();
v[t] = false;
} while(t != x);
}
}
void afs(int x, int sum) {
sum += vi[x];
if (sum >= 2) {
cout << "Yes";
exit(0);
}
if (vis[x]) return;
vis[x] = true;
v[x] = true;
for (int i = h[x]; i; i = e[i].ne) {
int u = e[i].t;
afs(u, sum);
vi[x] = max(vi[x], vi[u]);
}
}
int main() {
freopen("travel.in", "r", stdin);
freopen("travel.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
int x, y;
for (int i = 1; i <= m; i++) {
cin >> x >> y;
if(x != y) add(x, y);
else vi[x]++;
}
for (int i = 1; i <= n; i++) {
if (!dfn[i]) Tarjan(i);
}
if (sum != n) {
cout << "Yes";
return 0;
}
memset(v, 0, sizeof(v));
for (int i = 1; i <= n; i++) {
if (!v[i]) afs(i, 0);
}
cout << "No";
return 0;
}
game 0pts
多测,赛时全输出 $ Yes $ 又搞了90pts。。。,赛后0pts;
打打表,或者用 map
套 vector
打暴力( vector
存的是状态),然后就得到结论:$ n $ 是偶数且两两相等时后手赢,否则先手赢;
至于证明:证:证毕。。。
因为有排序,所以时间复杂度:$ \Theta(Tn \log n) $;
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int t;
int n;
int a[500005];
int main() {
freopen("game.in", "r", stdin);
freopen("game.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> t;
while(t--) {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
if (n & 1) {
cout << "Yes" << '\n';
continue;
}
sort(a + 1, a + 1 + n);
bool vis = true;
for (int i = 1; i <= n; i += 2) {
if (a[i] != a[i + 1]) {
vis = false;
break;
}
}
if (vis) cout << "No" << '\n';
else cout << "Yes" << '\n';
}
return 0;
}