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;
}