GRYZ10.19模擬賽解題報告


目錄

寫在前面

今天洛谷打卡大凶。

“忌考試,忌裝弱”,感覺很慌。

期望得分:\(100+100+100=300pts\)
實際得分:\(100+100+100=300pts\)

不要FST啊!!!!!

沒有大樣例差評。

CSP有四道題,為什么模擬賽只給三道?

下面來分享一下我做題過程:

14:10 下發題面
14:13 讀完 T1,感覺可以秒了它。
14:15 T2 不會扔掉,然后發現 T3 是非常經典的次短路(原汁原味)。
14:23 T1 寫完了,開始寫 T3 (因為忘記怎么做了只能邊寫邊yy)
14:34 T3 寫完了,一遍過了樣例,感覺這個樣例很弱,可能自己又要掛分了
14:35 感覺 T2 很像一個背包,然后二分答案在外面套起來,這樣復雜度是 \(\mathcal O(n^2 \log V)\) 的,極限在 \(1.2e8\) 左右,我感覺機房的評測機不一定能跑過去。其他地方感覺也不好優化了,於是開始寫。
14:45 T2 寫完了,稍微跳跳過了樣例
15:15 把 T1 的暴力和對拍寫完開始拍。
15:26 感覺有點無聊開始寫今天的解題報告
15:50 解題報告寫完了
16:30 感覺有點餓,出去吃了條脆脆鯊,但是被 ycc 發現了
17:30 一個小插曲:距離結束還有 10min,zzg 的電腦藍屏了哈哈哈哈,大杯。
17:40 另一個小插曲:結束了我第一個把用飛鴿把代碼發過去,一會兒 斜攬殘簫 過來跟我說,來來來我好不容易手造了一個數據,專門卡什么什么的,我給你試一試。然后呢我把 freopen 注釋掉試了試,但是我大意了,這個時候老師還沒有接收我的代碼,作為第一個發過去的被壓在了最下面,然后接收的時候已經把 freopen 注釋掉了,然后我就十分烏龍的掛掉了 T2 /ll
17:45 聽說 Chen_怡 半小時 AK 了這場比賽,我只能 Orz。

T1

肯定不能像他說的那樣枚舉子集啊。

然后你轉化一下方向,從前到后確定每一位,把 \(n\) 個串的某一位放到一起考慮,然后你發現可以直接統計某一位 \(1\) 的個數或者 \(0\) 的個數,然后通過判斷多少直接確定出填什么是最優的。每一位是相互獨立的,所以這樣做沒有問題。

時間復雜度 \(\mathcal O(nL)\),瓶頸在讀入。

/*
Work by: Suzt_ilymics
Problem: 不知名屑題
Knowledge: 垃圾算法
Time: O(能過)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 1e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;

int n, L;
char s[MAXN];
int a[MAXN], b[MAXN];

int read(){
    int s = 0, f = 0;
    char ch = getchar();
    while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
    while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
    return f ? -s : s;
}

int main()
{
    freopen("curse.in","r",stdin);
    freopen("curse.out","w",stdout);
	n = read();
	for(int i = 1; i <= n; ++i) {
	    cin >> s + 1;
	    L = strlen(s + 1);
	    for(int j = 1; j <= L; ++j) {
	        a[j] += (s[j] == '0');
        }
    }
    for(int i = 1; i <= L; ++i) {
        if(a[i] >= n - a[i]) {
            b[i] = 0;
        } else {
            b[i] = 1;
        }
    }
    for(int i = 1; i <= L; ++i) {
        printf("%d", b[i]);
    }
    puts("");
    return 0;
}

T2

看完題面,就能感覺出來它需要二分這個 \(L\)

因為你有兩種法杖,你需要確定怎么去使用這些神光是最優的。然后你很自然的想到了 DP,而且他還很像背包 DP。

先對法壇的位置排序,方便后面的處理。

然后你發現 \(R,G\) 的范圍很大,但是你想了想,發現它就是來嚇唬你的。

\(R + G \ge n\) 的時候,直接輸出 \(1\) 就行。

然后不能直接判斷的情況就是 \(R + G \le 2000\) 的情況了。

這樣的情況下足夠支持你開一個二維數組。你想了想發現可以這樣設狀態:

\(f_{i,j}\) 表示已經消滅了前 \(i\) 個法壇,用了 \(j\) 次紅色神光的情況下,最少用了多少次綠色神光。

轉移方程:

\[f_{i,j} = \min (f_{i,j}, f_{l1,j-1}) \]

\[f_{i,j} = \min (f_{i,j}, f_{l2,j}+1) \]

我們讓 \(a_i\) 作為光芒籠罩的右端點,因為每次神光可能會籠罩多個法壇,所以轉移的時候要從上一個不能籠罩的法壇的位置轉移。

其中 \(l1,l2\) 就分別表示紅色神光和綠色神光上一個不能籠罩的位置。

你在把 \(a\) 數組排序后可以直接用 lower_bound 查一下這個位置,但是這會平白無故多一個 \(\log\),可能只有我這個傻逼會想到這么拉的做法吧。

然后你發現,每次 DP 時 \(L\) 是固定的,所以 \(l1,l2\) 的變化一定是單調遞增的,然后你就發現你可以把他們當做指針,在不合法的時候暴力右移就可以了,很顯然最多會移動 \(n\) 次。

然后就做完了。

時間復雜度 \(\mathcal O(nR \log V)\)

/*
Work by: Suzt_ilymics
Problem: 不知名屑題
Knowledge: 垃圾算法
Time: O(能過)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 2021;
const int INF = 1e9+7;
const int mod = 1e9+7;

int n, R, G;
int a[MAXN];
int f[2021][2021];

int read(){
    int s = 0, f = 0;
    char ch = getchar();
    while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
    while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
    return f ? -s : s;
}

bool Check(int lim) {
    for(int i = 1; i <= n; ++i) {
        for(int j = 0; j <= R; ++j) {
            f[i][j] = 0x3f3f3f3f;
        }
    }
//    cout<<"new start !\n";
//    cout<<lim<<" \n";
    f[0][0] = 0;
    int l1 = 0, l2 = 0;
    for(int i = 1; i <= n; ++i) {
        while(l1 < n && a[l1 + 1] <= a[i] - lim) l1++;
        while(l2 < n && a[l2 + 1] <= a[i] - 2 * lim) l2++;
//        cout<<l1<<" "<<l2<<" "<<i<<" \n";
        for(int j = 0; j <= R; ++j) {
            if(j) f[i][j] = min(f[i][j], f[l1][j - 1]);
            f[i][j] = min(f[i][j], f[l2][j] + 1);
        }
    }
    for(int i = 0; i <= R; ++i) {
        if(f[n][i] <= G) return true;
    }
    return false;
}

int main()
{
    freopen("light.in","r",stdin);
    freopen("light.out","w",stdout);
	n = read(), R = read(), G = read();
	for(int i = 1; i <= n; ++i) {
	    a[i] = read();
    }
    sort(a + 1, a + n + 1);
    if(R + G >= n) {
        puts("1");
        return 0;
    }
    int l = 1, r = 1000000000, ans = r;
    while(l <= r) {
        int mid = (l + r) >> 1;
//        cout<<l<<" "<<mid<<" "<<r<<"\n"; 
        if(Check(mid)) ans = mid, r = mid - 1;
        else l = mid + 1;
    }
    printf("%d\n", ans);
    return 0;
} 

T3

次短路板子題吧。

考慮在存最短路 \(dis\) 的同時記錄一個次短路 \(Dis\)

因為一個點可能更新多次,所以我選擇 SPFA,並且點數和邊數看上去不是很多,應該不好卡。(卡到 \(\mathcal O(nm)\) 也不是不可以?)

更新的時候需要分類討論一下。

不管最短路還是次短路更新的時候都將其入隊。

然后就做完了。

/*
Work by: Suzt_ilymics
Problem: 不知名屑題
Knowledge: 垃圾算法
Time: O(能過)

希望別假掉!!! 

*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 2e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;

struct edge {
    int to, w, nxt;
}e[MAXN << 1];
int head[MAXN], num_edge = 1;

int n, m;
int dis[MAXN], Dis[MAXN];
bool vis[MAXN];
queue<int> q;

int read(){
    int s = 0, f = 0;
    char ch = getchar();
    while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
    while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
    return f ? -s : s;
}

void add_edge(int from, int to, int w) { e[++num_edge] = (edge){to, w, head[from]}, head[from] = num_edge; }

void SPFA() {
    memset(dis, 0x3f, sizeof dis);
    memset(Dis, 0x3f, sizeof Dis);
    dis[1] = 0, vis[1] = true, q.push(1);
    while(!q.empty()) {
        int u = q.front(); q.pop();
        vis[u] = false;
        for(int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            int w1 = dis[u] + e[i].w, w2 = Dis[u] + e[i].w;
            if(dis[v] > w1) {
                dis[v] = w1;
                if(!vis[v]) q.push(v);
            } else if(dis[v] == w1) {
                if(Dis[v] > w2) {
                    Dis[v] = w2;
                    if(!vis[v]) q.push(v);
                }
            } else {
                if(Dis[v] > w1) {
                    Dis[v] = w1;
                    if(!vis[v]) q.push(v);
                }
            }
        }
    }
}

int main()
{
    freopen("maze.in","r",stdin);
    freopen("maze.out","w",stdout);
	n = read(), m = read();
	for(int i = 1, u, v, w; i <= m; ++i) {
	    u = read(), v = read(), w = read();
	    add_edge(u, v, w), add_edge(v, u, w);
    }
    SPFA();
    printf("%d\n", Dis[n]);
    return 0;
}
/*

in: 
5 7
1 2 5
1 5 10
1 3 1
1 4 5
2 4 3
4 5 5
3 4 4

out:
12

1 -> 3 -> 1 -> 3 -> 4 -> 5
*/


免責聲明!

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



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