看題
有外校的一起考, 那我爆個 \(0\)
\(\rm{A}\)
至少不能是簡單題
考慮找規律一類的東西, 看能不能推出來?
\(\rm{B}\)
啊?
也是需要腦子, 多半不會做, 應該也是規律題
\(\rm{C}\)
至少暴力可以打, 爭取達到高檔暴力
\(\rm{D}\)
能打到這在想吧
完了嘛
時間分配: \(1 \rm{h} + 30 \rm{min} + 45 \rm{min} + 1 \rm{h} 15 \rm{min}\)
\(\rm{A}\)
手玩樣例看能不能找到明顯的性質
沒有什麼頭緒, 顯然的, 對於 \(30\%\) 的資料, 我們可以更改之後再跑一遍
考慮最佳化, 感性理解上, 每次只改變一個地方的交換, 肯定有大量重複的計算, 考慮最佳化掉這些重複的計算
-
前面沒改的
這顯然是不需要重複計算的, 我們透過簡單的二分操作, 就知道 \(id\) 位置的人在 \(t\) 操作前在哪裡 -
後面沒改的
這一部分比較複雜, 但是顯然的, 我們知道如果這個操作沒有涉及到 \(id\) 位置的人在 \(t\) 操作前所在位置, 那麼對答案沒有影響
考慮涉及到了的情況, 我們假設 \(id\) 當前位置為 \(u\), 位移之後位置為 \(v\)
那麼有, \(v\) 本來在後面的操作全部都位移到了 \(u\) 身上, 我們直接利用 \(v\) 的操作即可
那麼思路大概就有了, 時間複雜度約為 \(\mathcal{O}(Q \log n)\) , 大概可以透過
那麼考慮程式碼的具體實現
維護 \(n\) 個位置變換連結串列, 記錄第 \(i\) 次變換時, 變換操作的編號以及當前所在的位置
顯然的, 空間複雜度為 \(\mathcal{O}(n)\)
記錄每個位置變換了多少次, 建立連結串列時, 考慮利用輔助陣列
對於具體的操作, 我們先找到 \(id\) 編號在 \(t\) 次操作之前, 最後一次所在位置, 如果更改之後兩編號 \(u, v\) 與最後一次所在位置無關, 那麼直接輸出最後一次答案, 如果有關, 那麼我們找到(假設 \(u\) 為當前節點最後一次所在位置) \(v\) 後續的最終節點即可
其中 \(v\) 後續的最終節點怎麼求?
問題轉化為找 \(t\) 次操作之前最後一個出現 \(v\) 的操作, 即 \(v\) 在 \(t\) 操作前到底是那個最初位置的人在, 然後直接輸出這個人的最終節點即可, 在當前的寫法中不好實現
考慮再建立一個連結串列, 我們考慮記錄每一個位置變換 \(id\) 的連結串列, 即記錄第 \(i\) 次更改時, 最後一個 \(id\) 的編號
那麼對於第一個操作, 我們在第一個連結串列裡面求, 對於第二個操作, 我們在第二個連結串列裡面求
現在主要問題在於連結串列的建立, 對於第一個連結串列, 我們記錄每一個位置對應最初的 \(id\) , 一直更改即可
對於第二個連結串列, 我們建立第一個連結串列的過程中即可處理
注意到, 噹噹前被替換的操作與 \(id\) 有關時, 我們還是需要從後面的推, 不能直接輸出
預期得分: \(100 \rm{pts}\)
\(\rm{B}\)
對於 \(30 \%\) 的資料, 考慮直接處理 \(x, y\) 座標的鏈, 每次刪一個就把其之後的全部推上來
預期得分: \(30 \rm{pts}\)
\(\rm{C}\)
注意到 \(m\) 很小啊, 考慮從這裡下手
顯然的, 本質不同的字串只有 \(2 ^ {m + 1} - 1\) 種, 對於 \(m \leq 10\) 已經可以處理了
考慮 \(m\) 更大的情況
考慮不出來
預期得分: \(30 \rm{pts}\)
\(\rm{D}\)
看不懂題, \(S\) 是啥?
預期得分: \(0 \rm{pts}\)
程式碼
預期得分: \(100 + 30 + 30 = 160 \rm{pts}\)
剩下 \(1 \rm{h }30 \rm{min}\) 打程式碼
\(\rm{A}\)
決定我今天到底有多少的題, 大吉保佑我
先理清楚思路
讀入直接用題面裡面的
首先是建立連結串列
第一個連結串列
維護原本在 \(id\) 位置的人變換順序
初始化時, 記錄 \(pos_{id} = id\) 表示 \(id\) 位置上的人是 \(id\)
每次對於一個變換 \(u, v\) , 考慮找到 \(pos_u, pos_v\), 然後對於連結串列 \(pos_u\) , 插入當前位置 \(v\) , 對於 \(pos_v\) , 插入當前位置 \(u\) , 令 \(pos_u, pos_v\) 交換
第二個連結串列
維護位置 \(id\) 上人的變換順序
在第一個連結串列的維護過程中, 每次 \(pos_{id}\) 變化, 將 \(pos_{id}\) 插入進 \(id\) 這一連結串列
注意兩個連結串列都要記錄在第幾次操作到達方便二分
其次是查詢
每次知道 \(t, id\) , 二分查詢 \(id\) 在 \(t\) 之前的最後位置, 檢查 \(u, v\) 中是否有 \(id\)
-
沒有
直接輸出最終答案 -
有
二分查詢 \(v\) 位置在 \(t\) 前最後一個所在人的編號, 輸出這個人的最終答案
\(\rm{C}\)
今天能拿到的所有分了
#include <bits/stdc++.h>
#define FILE_IO
const int MAXN = 1e5 + 20;
const int MAXSIZ = 3000;
int n, m;
/*n <= 1000*/
class Subtask1
{
private:
std::string Now[MAXN];
int NowNum[MAXN];
int Calc(int u, int v)
{
int x = u ^ v;
int ans = 0;
while (x)
{
if (x & 1) ans++;
x >>= 1;
}
return ans;
}
public:
void solve()
{
for (int i = 1; i <= n; i++) {
std::cin >> Now[i];
}
for (int i = 1; i <= n; i++) {
NowNum[i] = 0;
for (int j = 0; j < Now[i].length(); j++)
{
if (Now[i][j] == 'G') {
NowNum[i] += (1 << j);
}
}
}
for (int i = 1; i <= n; i++) {
int Ans = 0;
for (int j = 1; j <= n; j++) {
Ans = std::max(Ans, Calc(NowNum[i], NowNum[j]));
}
printf("%d\n", Ans);
}
}
} S_1;
/*m <= 10*/
class Subtask2
{
private:
int Have[MAXSIZ];
int Ans[MAXSIZ];
std::string Now[MAXN];
int Calc(int u, int v)
{
int x = u ^ v;
int ans = 0;
while (x)
{
if (x & 1) ans++;
x >>= 1;
}
return ans;
}
public:
void solve()
{
for (int i = 1; i <= n; i++) {
std::cin >> Now[i];
int NowNum = 0;
for (int j = 0; j < Now[i].length(); j++)
{
if (Now[i][j] == 'G') {
NowNum += (1 << j);
}
}
Have[NowNum]++;
}
for (int i = 0; i < (1 << (m + 1)); i++) {
if (!Have[i]) continue;
Ans[i] = 0;
for (int j = 0; j < (1 << (m + 1)); j++) {
if (!Have[j] || i == j) continue;
Ans[i] = std::max(Ans[i], Calc(i, j));
}
}
for (int i = 1; i <= n; i++) {
int NowNum = 0;
for (int j = 0; j < Now[i].length(); j++)
{
if (Now[i][j] == 'G') {
NowNum += (1 << j);
}
}
printf("%d\n", Ans[NowNum]);
}
}
} S_2;
int main()
{
#ifdef FILE_IO
freopen("different.in", "r", stdin);
freopen("different.out", "w", stdout);
#endif
scanf("%d %d", &n, &m);
if (m <= 10) {
S_2.solve();
} else {
S_1.solve();
}
return 0;
}