2021年第19屆浙江省程序設計競賽題解&比賽心得


2021年第19屆浙江省程序設計競賽題解&比賽心得

這篇是和隊友一起出的一份題解,這次省賽打的算是成功,但是也有遺憾,最后的I題有一種特況沒考慮到,主要是沒有實物不好模擬,全靠想象也不好搞(還是我們太菜了)。最后Cu首收尾。希望下次省賽沖波Au吧。
隊友在寫題解了,會慢慢更新的。
計划是除了EHK都試着補一下。
進入正題

F. Fair Distribution
題意:T組數據,每組數據有a,b兩個數,其中a只能減,b只能加,每次加一或者減一的代價是1,問使最后b % a == 0的代價最小是多少。
方法:暴力, 數學
兩種情況
1、\(a\ge b\)時顯然答案是\(a - b\)
2、\(a < b\)時,最后的\(a\)一定是在\([1, \sqrt b]\)或者\(\frac{b}{a}\)\([1, \sqrt b]\)內,所以對\(b\)開根號然后暴力驗證就行
做一個簡單的說明吧,其實很容易看的出來,假設b不變的時候,我們剛剛的想法肯定是沒問題的。當b要增加的時候,最壞情況下就是a = b - 1, b要增加兩倍,但是最后的\(\frac {b}{a}\)還是在\(\sqrt{b}\)內。(不嚴謹,但是應該能理解其正確性)
接下來給出代碼

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL,LL> PLL;
const int INF = 0x3f3f3f3f, N = 1e5 + 10;
inline int lc(int u) {return u << 1;}
inline int rc(int u) {return u << 1 | 1;}

inline void solve() {
    LL a, b; cin >> a >> b;
    if (a >= b) cout << a - b << endl;
    else {
        LL res = 1e9 + 10;
        LL k = (LL)sqrt(b);
        for (int i = 1; i <= k; i ++ ) {
            if (b % i) {
                if (a >= i)
                    res = min(res, a - i + abs((b + i - 1) / i * i - b));
                if (a >= (b + i - 1) / i) {
                    LL now = (b + i - 1) / i;
                    res = min(res, abs(a - now) + abs((b + now - 1) / now * now - b));
                }
            } else {
                if (a >= i)
                    res = min(res, a - i);
                if (a >= b / i)
                    res = min(res, abs(a - b / i));
            }
        }
        cout << res << endl;
    }
}
int main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int t; cin >> t;
    while (t -- )
        solve();
    return 0;
}

G.Wall Game

隊友寫的題解

J. Grammy and Jewelry

題意:給你一張圖,每個點上面都有無窮個寶石,每個寶石都有自己的價值,同一個點上的寶石價值一樣。邊權為1,每次可以從點1出發去某個點拿走一個寶石然后回到1號點, 當且僅當你去那個點拿到一個寶石並且回到起點才算得到這個寶石的價值, 手上最多只能有一個寶石。每次經過一條邊的耗時是1,問在時間T內的每個時間點可以得到的價值最多是多少。
方法:BFS,完全背包
稍微想一下容易得出是一個完全背包,設\(d[u]\)表示u點到1的距離,於是就變成了一個完全背包的問題。n個物品,每個物品有無限個,你有一個總體積為T的背包,每類物品的代價是\(2 \times d[u]\),價值是\(a[u]\)。就是一個板子題了。先BFS預處理出d數組,然后套板子即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL,LL> PLL;
const int INF = 0x7f7f7f7f, N = 1e5 + 10;
inline int lc(int u) {return u << 1;}
inline int rc(int u) {return u << 1 | 1;}
int a[N], d[N];
int n, m, t;
int h[N], val[N * 2], nex[N * 2], idx;
LL dp[N];
void bfs() {
    memset(d, 0x7f, sizeof d);
    queue<int> q;
    q.push(1);
    d[1] = 0;
    while (!q.empty()) {
        int tt = q.front();
        q.pop();
        for (int i = h[tt]; ~i; i = nex[i]) {
            int j = val[i];
            if (d[j] != INF) continue;
            d[j] = d[tt] + 2;
            q.push(j);
        }
    }
}
inline void add(int x, int y) {
    val[idx] = y, nex[idx] = h[x], h[x] = idx ++;
}
inline void solve() {
    memset(h, -1, sizeof h);
    cin >> n >> m >> t;
    for (int i = 2; i <= n; i ++ ) cin >> a[i];
    while (m -- ) {
        int x, y; cin >> x >> y;
        add(x, y), add(y, x);
    }
    bfs();
    for (int i = 1; i <= n; i ++ ) {
        for (int j = d[i]; j <= t; j ++ )
            dp[j] = max(dp[j], dp[j - d[i]] + a[i]);
    }
    for (int i = 1; i <= t; i ++ ) cout << dp[i] << ' ';
    cout << endl;
}
int main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
//    int t; cin >> t;
//    while (t -- )
        solve();
    return 0;
}

L.String Freshman

題意:有一個憨憨寫了一個KMP的假算法,然后給你一個模式串,問你能不能出一個匹配串把這個代碼卡WA掉。
方法:KMP
其實還是比較好理解的,看代碼就知道這里和標准的KMP代碼有一個不一樣,就是KMP是如果不匹配就跳到nex數組中,而給出的代碼是直接跳到起點,所以如果我們的nex數組中有一個nex值不是跳到起點的,就可以構造出這樣的字符串把他hack掉。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL,LL> PLL;
const int INF = 0x3f3f3f3f, N = 1e5 + 10;
inline int lc(int u) {return u << 1;}
inline int rc(int u) {return u << 1 | 1;}
char s[N];
int nex[N];
inline void solve() {
    int n; cin >> n;
    scanf("%s", s + 1);
    for (int i = 2, j = 0; i <= n; i ++ ) {
        while (j && s[i] != s[j + 1]) j = nex[j];
        if (s[i] == s[j + 1]) j ++ ;
        nex[i] = j;
    }
    for (int i = 1; i <= n; i ++ ) {
        if (nex[i] != 0) {
            puts("Wrong Answer");
            return ;
        }
    }
    puts("Correct");
}
int main() {
//    ios::sync_with_stdio(false), cin.tie(nullptr);
//    int t; cin >> t;
//    while (t -- )
        solve();
    return 0;
}

M. Game Theory

題意:老師和學生各出一個數字,范圍在\([1, 20]\),假設老師出了x,學生出了y,就有接下來幾步。
1、老師給學生x分
2、學生給老師y分
3、如果\(x > y\)學生多給老師10分
4、如果\(y > x\)老師多給學生10分
5、如果平局,雙方均不用多給對方分。
注意,這里的給分數是從自己這邊扣一些然后給對面的。
問:如果老師隨機出值,每個學生可以自己選擇值,問面對n個學生的情況下(對每個學生都是獨立情況,也就是對上一個學生老師出的值和對這一個學生出的值是相互獨立事件)老師的期望得分是多少。
賽場上第一眼看到數學期望直接爬了,結果一看通過率不少,就回頭仔細研究了一下,列了下公式就出來了,下面是過程
我們容易看出,分數不會無故產生,都是老師和學生之間的流通,因此老師的期望得分就是學生的期望得分的負數。
對任意一輪,設老師出的值是x,學生是y,站在角度講,得分的情況是
老師贏:\(-x + y + 10\) \(x \in [y + 1, 20]\)
老師負:\(-x + y - 10\) \(x \in [1, y - 1]\)
平局:\(-x + y = 0\) \(x = y\)
再用我們小學二年級就學過的知識求期望(其實就是平均值)
\(E = (y -x + 10) \frac{20 - y}{20} + (y - x - 10) \frac{y - 1}{20}\)
化簡得
\(E = \frac{210 - 19x - y}{20}\)
這是老師的得分期望,這里似乎還看不出啥,但是學生的期望就是\(-E\)
\(-E = \frac{y + 19x - 210}{20}\)
這里其實就看的出來了,學生可以自己決定y的大小,於是y一定是出最大為最優
那么就簡單了,因為老師是等概率出數字的,於是出的x的期望一定是10,帶入
\(E = 0\)
輸出0即可

//我都講的這么清楚了還要看代碼么QAQ


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM