1. 程式人生 > 實用技巧 >20200723比賽總結

20200723比賽總結

下面是我對20200723的題解

T1

這一道題確定了我們可以改變矩形的長寬(也就是\(K*K\)的矩形)

所以我們自然而然地考慮到將我們以\(O(n^3)\)的時間複雜度來列舉矩形+判斷答案貢獻轉化成用二維的差分+字首和來將每個點作為矩形的左上角時可以對答案作出的貢獻統計下來,至此,\(O(n^3)-->O(n^2)\)

部分細節看程式碼

程式碼如下:

#include<bits/stdc++.h>
using namespace std;

const int N = 2005;
int n, k;
char a[N][N];
int up[N], dwn[N], l[N], r[N];
int cnth[N][N], cnts[N][N], ans;

int main() {
	scanf("%d%d", &n, &k);
	for(int i = 1; i <= n; i++) up[i] = l[i] = n + 1;
	for(int i = 1; i <= n; i++) {
		scanf("%s", a[i] + 1);
		for(int j = 1; j <= n; j++) {
			if(a[i][j] == 'B') 
				l[i] = min(l[i], j), up[j] = min(up[j], i),
				r[i] = max(r[i], j), dwn[j] = max(dwn[j], i);
		}
	}
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= n; j++) {
			cnth[i][j] = cnth[i - 1][j] + (l[i] >= (j - k + 1) && (r[i] <= j) && (l[i] != n + 1));
			cnts[i][j] = cnts[i][j - 1] + (up[j] >= (i - k + 1) && (dwn[j] <= i) && (up[j] != n + 1));
		}
		ans += (l[i] == n + 1) + (up[i] == n + 1);
	}
	int maxn = 0;
	for(int i = k; i <= n; i++) {
		for(int j = k; j <= n; j++) {
			maxn = max(maxn, cnth[i][j] - cnth[i - k][j] + cnts[i][j] - cnts[i][j - k]);
		}
	}
	printf("%d\n", ans + maxn);
	return 0;
}

T2

我們分析題目可以發現,對於一個連續上升或連續下降的連續的子序列,你將這個子序列劃分成兩個對於答案並沒有影響。

但是將一個曲線從最高點或者最低點劃開是更優的。

所以我們可以考慮\(O(n)\)的dp,dp是討論在每一個極點處是在左邊劃分還是右邊劃分。

現在程式碼如下:

#include<bits/stdc++.h>
using namespace std;
int read() {
    char c=getchar();while(c!='-'&&!isdigit(c)) c=getchar();
    int neg=0;if (c=='-') neg=1,c=getchar();
    int num=0;while(isdigit(c)) num=num*10+c-'0',c=getchar();
    return neg?-num:num;
}
int a[10000001], b[10000001];
long long ans[2];
int main() {
    int n = read();
    int x, y, z, m;
    x = read(), y = read(), z = read(), b[1] = read(), b[2] = read(), m = read();
    int p = 0;
    for (int i = 3; i <= n; i++) b[i] = (1ll*x*b[i-1]+1ll*y*b[i-2]+z) & ((1<<30)-1);
    for (int i = 1; i <= m; i++) {
        int np, l, r;
        np = read(), l = read(), r = read();
        while (p < np) {
            ++p;
            a[p] = b[p] % (r - l + 1) + l;
        }
    }
    ans[0] = -0x3f3f3f3f3f3f3f3f, ans[1] = 0;
    int maxn = 0, minn = 0x3f3f3f3f;
    for (int i = 1; i <= n; i++)
    	/*
			p記錄的是上一個極點位置在哪裡,初始化為p=n,因為最開始時並沒有極值
			ans[0]記錄的是上一個極值是劃分在左邊
			ans[1]記錄的是上一個極值是劃分在右邊		
		*/
        if (i > 1 && i < n && ((a[i] > a[i-1] && a[i] >= a[i+1]) || (a[i] < a[i-1] && a[i] <= a[i+1]))) {
            long long nxt[2] = {-0x3f3f3f3f3f3f3f3f, -0x3f3f3f3f3f3f3f3f};
            nxt[0] = max(nxt[0], ans[0] + max(maxn, a[p]) - min(minn, a[p]));
            if (p != i - 1) nxt[0] = max(nxt[0], ans[1] + maxn - minn);//為什麼有這個限制條件
			//因為如果p==i-1,說明這一個點是一個點自己構成的子序列,不對答案做貢獻 
            else nxt[0] = max(nxt[0], ans[1]);
            nxt[1] = max(nxt[1], ans[0] + max(maxn, max(a[p], a[i])) - min(minn, min(a[p], a[i])));
            nxt[1] = max(nxt[1], ans[1] + max(maxn, a[i]) - min(minn, a[i]));
            ans[0] = nxt[0], ans[1] = nxt[1];
            maxn = 0, minn = 0x3f3f3f3f, p = i;
        }
        else maxn = max(maxn, a[i]), minn = min(minn, a[i]);
    cout << max(ans[0] + max(maxn, a[p]) - min(minn, a[p]), ans[1] + maxn - minn) << endl;
}

T3

臣妾做不到啊。

啊,這。