1. 程式人生 > >codeforces 980非官方題解

codeforces 980非官方題解

來自一名div3+選手的題解,非完全版。

這題本質上是一個數學問題。

#include <stdio.h>
#include <iostream>
#include <string>
using namespace std;

int main() {
    string s;
    cin >> s;
    int cnt1 = 0;
    int cnt2 = 0;
    for(int i = 0; i < s.length(); i++) {
        if(s[i] == '-') {
            cnt1++;
        } else
{ cnt2++; } } if(cnt2 == 0) { printf("YES"); return 0; } if(cnt1 % cnt2 == 0) { printf("YES"); } else { printf("NO"); } return 0; }

這題應該就是要構造一個對稱的情況。我讀錯題目了,是說如果道路相鄰,那麼可以通過,而不是,所有的旅館要連著。

那麼按k的奇數偶數來排一下。如果是偶數,那麼在(2,2), (2, n-1)開始一直從兩邊往中間擺。如果第2行擺滿了,那麼就擺第三行。然後要特判k取

2×(n2) 的情況,這時候兩個中間要填的。如果是奇數,那麼就填一箇中間的。

當然我自己不是這樣寫的,我的寫法是,奇數是從中間往兩邊填,如果還沒填夠再從兩邊往中間填,但是偶數的情況下,直接第2行,第3行,都從第2個開始,往右邊填。我這種寫法是因為,我讀錯題目了。但是我這種做法確實是對的。首先偶數的情況,這是根據第二行與第三行的中間一行為對稱軸的圖形,那麼顯然是可以的。對於奇數的情況,(1,1)->(4, n)的最短路徑數量與(1,n)->(4,1)的最短路徑數量是一樣的。然後(4,1)->(1,n)的最短路徑數量與(1,n)->(4,1)的最短路徑數量也是一樣的。

#include <stdio.h>
#include <iostream>

using namespace std;

int n, k;
const int maxn = 110;
char a[maxn][maxn * 2];
int main() {
    scanf("%d%d", &n, &k);
    int max = (n - 2) * 2;
    if(k > max) {
        printf("NO");
        return 0;
    }

    for(int i = 0; i < 4; i++) {
        for(int j = 0; j < n; j++) {
            a[i][j] = '.';
        }
    }
    if(k % 2 == 0) {
        for(int i = 1; i <= k / 2; i++) {
            a[1][i] = '#';
            a[2][i] = '#';
        }
    } else {
        a[1][n/2] = '#';
        k--;
        int i1 = n / 2 - 1;
        int i2 = n / 2 + 1;
        while(i1 > 0 && k > 0) {
            a[1][i1] = '#';
            a[1][i2] = '#';
            i1--;
            i2++;
            k -= 2;
        }
        i1 = 1;
        i2 = n - 2;
        while(k > 0) {
            a[2][i1] = '#';
            a[2][i2] = '#';
            i1++;
            i2--;
            k -= 2;
        }
    }
    printf("YES\n");
    for(int i = 0; i < 4; i++) {
        for(int j = 0; j < n; j++) {
            printf("%c", a[i][j]);
        }
        printf("\n");
    }
    return 0;
}

這個講究的就是一個貪心。具體的實現可能稍微有點複雜。對於每一個數,我們要從當前數字出發,看能不能放最小值為這個數的組裡面,一直到可能的最小值,不能把這個過程反過來。然後把可以放的組的最小值到當前數字,都放入組中。

#include <stdio.h>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 1e5 + 10;
int a[maxn];
int b[maxn];
int main() {
    int n, k;
    scanf("%d%d", &n, &k);
    memset(b, -1, sizeof b);
    for(int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
    }
    for(int i = 1; i <= n; i++) {
        int x = a[i];
        if(b[x] != -1) {
            continue;
        }
        int s = x - k + 1;
        s = max(0, s);
        int t = x;
        for(int j = x; j >= s; j--) {
            if(b[j] == -1) {
                t = j;
            } else if(x - b[j] + 1 <= k) {
                t = b[j];
                break;
            } else {
                break;
            }
        }
        for(int j = t; j <= x; j++) {
            b[j] = t;
        }
    }
    for(int i = 1; i <= n; i++) {
        printf("%d ", b[a[i]]);
    }
    return 0;
}

這題主要是要能讀懂題目。

首先介紹了一個性質f,使得一個數組A對應的對映值為t,那麼有f(A)=t。對於給定的陣列B,有若干種取其連續子序列的做法,使得得到 Bx ,對於每個Bx ,有一個f(Bx)=tx ,要求統計tx 各出現了多少次。

對於這裡的性質,我在程式碼的註釋裡面有詳細的講解。

#include<cstdio>
#include<map>

using namespace std;

#define ran 5555

int n;
int a[ran], s[ran], pre[ran];
map<int, int> mp;

int main() {
    // 獲取輸入並預處理輸入
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        int f = (a[i] < 0) ? -1 : 1;
        a[i] *= f;
        // 如果一個數可以分解為a*b^x*... 其中x>=2,那麼這時候x可以變成x-2,因為這個數a1與別的數a2相乘的時候如果為平方數,那麼這個數a1除以一個平方數與a2相乘仍然是平方數
        // 所以這時候,實際上要統計的就是,在處理了之後,子序列中有多少個不一樣的數,就要分成多少個組,對於0的情況下面有特判
        for (int j = 2; j * j <= a[i]; j++) {
            while (a[i] % (j * j) == 0) {
                a[i] /= j * j;
            }
        }
        a[i] *= f;
    }

    // 找出相鄰的在i號數前面的一樣的數的序號
    mp.clear();
    for (int i = 1; i <= n; i++) {
        pre[i] = mp[a[i]];
        mp[a[i]] = i;
    }

    // i表示連續子序列的開始位置(包括)
    for (int i = 1; i <= n; i++) {
        // cur表示除了0以外有多少個不同的數
        int cur = 0;
        // j表示連續子序列的結束位置(包括)
        for (int j = i; j <= n; j++) {
            // a[j] != 0表示,計算有多少個不同的數的時候,並不在意有沒有0,因為0放哪個組裡面都行。而如果只有0的話,下面的s[max(1, cur)]++;已經處理好了這種情況
            // pre[j] < i 表示,第j號元素前一次出現是在i之前,也就是說,在這裡是第一次出現,也就是說,多出現了一個不同的數
            if (a[j] != 0 && pre[j] < i) {
                cur++;
            }
            // 如果有0個數,那麼實際上也是要分成1組
            s[max(1, cur)]++;
        }
    }
    // 輸出
    for (int i = 1; i <= n; i++) {
        printf("%d%c", s[i], " \n"[i >= n]);
    }

    return 0;
}

據說用線段樹或者樹狀陣列都可以,但是我看了題解還是不能理解到底是怎麼實現的,以後有機會補一補吧。