CSP-S 2019 第二輪 DAY2 簡單解析(含部分分代碼)

CSP-S 2019第二輪第二試昨天上午正式結束了,賽後仔細拜讀了DAY2的三個題目,難!但也是明年NOI選手不錯的試煉機會,高分選手大概率也是明年NOI賽場上的選手。

 

第一題:Emiya 家今天的飯,比較難的動態規劃

第二題:劃分,動態規劃,高精度,單調隊列

第三題:樹的重心,dfs序,線段樹

 

以下代碼均在oitiku測試,第一題Emiya 家今天的飯,O(n^2m)的算法僅得到84分,超時了4個點,理論上應該是可以通過的:)第二題劃分,由於數據太大,寫了高精度,寫的不是太好,最後三個點在oitiku上MLE了,僅得到88分,之後再來改進:)第三題僅寫了一下大致想法,往後再來補坑

 

 

CSP-S 2019 D2T1 Emiya 家今天的飯

思路:容斥,方案計數,對998, 244, 353取餘數,已經在預示着這題是動態規劃了。先考慮無限制情況下,顯然是一個普及組難度的dp問題,然後考慮k/2的限制,我們可以從總的方案中減去不符合k/2限制的方案數即可。

代碼如下:

 

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int maxn = 210;
const int maxm = 2100;
const long long mod = 998244353;

long long g[maxn][maxn], f[maxn][maxn], a[maxn][maxm], line[maxn];
int n, m;

int main() {
	freopen("meal.in", "r", stdin);
	freopen("meal.out", "w", stdout);

	cin >> n >> m;
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j) {
			cin >> a[i][j];
			a[i][j] %= mod;
			line[i] = (line[i] + a[i][j]) % mod;
		}

	long long sum = 0;
	g[0][0] = 1; 
	for (int i = 1; i <= n; ++i)
		for (int j = 0; j <= i; ++j) {
			g[i][j] = g[i - 1][j];
			for (int k = 1; k <= m; ++k) {
				if (j > 0)
					g[i][j] += g[i - 1][j - 1] * a[i][k] % mod;
				g[i][j] %= mod;
			}
		}

	for (int i = 1; i <= n; ++i)
		sum = (sum + g[n][i]) % mod;

	for (int l = 1; l <= m; ++l) {
		memset(f, 0, sizeof(f));

		// 差值可能爲負數,偏移量爲n + 1
		f[0][n + 1] = 1;
		for (int i = 1; i <= n; ++i)
			for (int j = n + 1 - i; j <= i + n + 1; ++j) {
				f[i][j] = f[i - 1][j];
				f[i][j] += (f[i - 1][j - 1] * a[i][l]) % mod;
				f[i][j] += (f[i - 1][j + 1] * ((line[i] - a[i][l] + mod) % mod)) % mod;
				f[i][j] %= mod;
			}

		for (int i = 1; i <= n; ++i) {
			sum = (sum - f[n][i + n + 1]) % mod;
			if (sum < 0)
				sum += mod;
		}

	}
	cout << sum << endl;
	fclose(stdin);
	fclose(stdout);
	return 0;
}





 

 

CSP-S 2019 D2T2 劃分

思路:根據數據規模顯然是要一個O(n)的算法,粗略的就是先貪心,然後典型的序列型動態規劃。dp一下n^2,必須使用單調隊列優化至O(n)。最後由於數據太大,需要用高精度或者別的取巧的方法優化一下,我們這裏使用傳統的高精度來寫一下:)

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int maxn = 4e7, maxm = 1e5;

int n, type, x, y, z, m;
int a[maxn], p[maxm], l[maxm], r[maxm], b[maxn + 1], q[maxn + 1], head, tail;
long long sum[maxn + 1];

struct BigInteger {
    
    int len, data[50];
    
    BigInteger(string& s) {
        memset(data, 0, sizeof(data));
        len = s.size();
        for (int i = 0; i < len; ++i)
            data[i] = s[len - i - 1] - '0';
    }
    
    void set(long long num) {
        len = 0;
        memset(data, 0, sizeof(data));
        while (num > 0) {
            data[len] = num % 10;
            len ++;
            num /= 10;
        }
        len ++;
    }

    BigInteger() {
        len = 0;
        memset(data, 0, sizeof(data));
    }
    
    BigInteger operator + (const BigInteger &b) {
        BigInteger c;

        c.len = 0;
        while(c.len <= max(len, b.len) + 1) {
            c.data[c.len] += data[c.len] + b.data[c.len];
            c.data[c.len + 1] += c.data[c.len] / 10;
            c.data[c.len] %= 10;
            c.len ++;
        }
        while (c.len > 0 && c.data[c.len] == 0)
            c.len --;

        c.len++;
        
        return c;
    }
    
    BigInteger operator * (const BigInteger &b) {
        BigInteger c;

        c.len = len + b.len - 1;
        for (int i = 0; i < len; ++i)
            for (int j = 0; j < b.len; ++j)
                c.data[i + j] += data[i] * b.data[j];
        
        for (int i = 1; i < c.len; ++i) {
            c.data[i] += c.data[i - 1] / 10;
            c.data[i - 1] %= 10;
        }
        
        while (c.data[c.len - 1] >= 10) {
            c.data[c.len] = c.data[c.len - 1] / 10;
            c.data[c.len - 1] %= 10;
            c.len += 1;
        }

        return c;
    }

    string toString() {
        string s = "";
        for (int i = len - 1; i >= 0; --i)
            s += (char)(data[i] + '0');
        return s;
    }
};

int main() {
    freopen("partition.in", "r", stdin);
    freopen("partition.out", "w", stdout);

    cin >> n >> type;
    if (type == 0) {
        for (int i = 0; i < n; ++i)
            cin >> a[i];
    } else {
        cin >> x >> y >> z >> a[0] >> a[1] >> m;
        for (int i = 2; i < n; ++i)
            a[i] = (1LL * x * a[i - 1] + 1LL * y * a[i - 2] + z) & 0x3fffffff;
        for (int i = 0; i < m; ++i)
            cin >> p[i] >> l[i] >> r[i];
        for (int i = 0, j = 0; i < n; ++i) {
            if (i == p[j]) ++j;
            a[i] = a[i] % (r[j] - l[j] + 1) + l[j];
        }
    }

    for (int i = 0; i < n; ++i)
        sum[i + 1] = sum[i] + a[i];

    q[tail++] = 0;
    for (int i = 0; i < n; ++i) {
        while (head + 1 < tail && 2 * sum[q[head + 1]] - sum[b[q[head + 1]]] <= sum[i + 1])
            ++head;
        b[i + 1] = q[head];
        while (head < tail && 2 * sum[i + 1] - sum[b[i + 1]] <= 2 * sum[q[tail - 1]] - sum[b[q[tail - 1]]])
            --tail;
        q[tail++] = i + 1;
    }

    BigInteger ans, tmp;
    for (int i = n; i > 0; i = b[i]) {
        tmp.set((long long)(sum[i] - sum[b[i]]));
        tmp = tmp * tmp;
        ans = ans + tmp;
    }
    cout << ans.toString() << endl;
    fclose(stdin);
    fclose(stdout);
    return 0;
}



 

CSP-S D2T3 樹的重心

思考:

https://codeforces.com/problemset/problem/686/D,恰好比賽前跟同學們也討論過子樹重心的問題。

 

樹的問題的確可以深挖很多有趣的性質,這也是我比較喜歡樹上的問題的一些原因。這可能也是爲什麼樹的題目出那麼多的原因之一吧。逆向思維一下,如果這點是樹的重心,那麼一定是刪除size最大的子樹裏面的邊。本質dfs序,然後用線段樹維護即可。

 

 

2019賽季告一段落,高潮已過,停課衝刺的同學們該調整心情迴歸課堂了,衝刺明年NOI的同學,革命尚未成功,同志仍需努力!

 

每一位同學都值得爲這次CSP-J/S賽寫一個總結,心得,感悟,亦或者是回顧自己的學習C++/算法競賽、OI的歷程。

 

燦爛的明天正在等着你們!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章