codeforces 980非官方題解
阿新 • • 發佈:2018-12-24
來自一名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行,第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。對於給定的陣列,有若干種取其連續子序列的做法,使得得到 ,對於每個 ,有一個 ,要求統計 各出現了多少次。
對於這裡的性質,我在程式碼的註釋裡面有詳細的講解。
#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;
}
據說用線段樹或者樹狀陣列都可以,但是我看了題解還是不能理解到底是怎麼實現的,以後有機會補一補吧。