1. 程式人生 > 其它 >2021暑假訓練1

2021暑假訓練1

A[CF1028A(800)]

發現矩陣中心可以用一些較為簡單的方式表示,這裡求出左上角和右下角即可算出中心。

#include <bits/stdc++.h>
using namespace std;
const int INF = numeric_limits<int> :: max() / 2;
int main() {
  int N, M;
  cin >> N >> M;
  vector<string> S(N);
  for (int i = 0; i < N; ++i) {
    cin >> S[i];
  }
  int A = INF;
  int B = INF;
  int C = -INF;
  int D = -INF;
  for (int i = 0; i < N; ++i) {
    for (int j = 0; j < M; ++j) {
      if (S[i][j] == 'B') {
        A = min(A, i);
        B = min(B, j);
        C = max(C, i);
        D = max(D, j);
      }
    }
  }
  cout << (A + C) / 2 + 1 << ' ' << (B + D) / 2 + 1 << '\n';
}
View Code

B[ARC065C(930)]

這個問題比較動態,所以考慮利用$dp$去做,設dp[i]表示是否能構成S[1...i],那麼轉移是把題目中給的串拼接在後面,判斷即可。

當然也可以使用貪心去做,本質上可以理解為將給出的串建立AC自動機然後在上面匹配,不過相比於$dp$稍微麻煩一些。

$dp$解法

#include <bits/stdc++.h>

using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    string s;
    cin >> s;

    int n = s.size();

    vector<int> dp;
    dp.assign(n + 1, 0);

    vector<string> S = {"dream", "dreamer", "erase", "eraser"};
    
    dp[0] = 1;

    for(int i = 0; i < n; ++i) {
        for(auto& t : S) {
            int len = t.size();
            if(s.substr(i, len) == t) {
                dp[i + len] |= dp[i];
            }
        }
    }

    if(dp[n]) {
        cout << "YES" << '\n';
    } else {
        cout << "NO" << '\n';
    }
    return 0;
}
View Code

C[CF1368D(1700)]

用到了一個較為常用的結論:(X|Y)+(X&Y)=X+Y,於是發現操作本質上是把某一個二進位制上的位從一個數傳給另一個數。考慮最優情況,根據高中數學可以得知,平方數的和最大,肯定是將值儘量集中,也就是儘量先造出大的數,於是貪心解決即可,詳見程式碼。這個結論也可以通過打表或手動構造發現,所以遇見一些題目的時候可以考慮打表。

#include <bits/stdc++.h>

using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    
int n; cin >> n; vector<long long> a(n); for(int i = 0; i < n; ++i) { cin >> a[i]; } vector<int> cnt(22); for(long long i : a) { for(int j = 0; j < 22; ++j) { if(i >> j & 1) { cnt[j] += 1; } } } long long ans = 0; for(int i = 0; i < n; ++i) { long long x = 0; for(int j = 21; j >= 0; --j) { if(cnt[j] > 0) { x += (1 << j); cnt[j] -= 1; } } ans += x * x; } cout << ans << '\n'; return 0; }
View Code

D[1205B(1900)]

由於$N$比較大,所以直接做比較困難。但是仔細觀察或手玩可以發現,如果存在三個數兩兩$and$不為$0$,那麼答案就是$3$,這個條件轉化一下也就是存在一個二進位制位出現在$3$個數上。那麼如果上面的條件不成立,可以推出一個二進位制位只出現兩次,那麼就可以根據二進位制位去建圖和連邊,問題轉化為求一張$128$個點的圖的最小環。關於最小環,利用$floyd$直接求出即可,具體詳見$oiwiki$。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
int n, f, cnt;
int mp[maxn], d[155][155], s[155][155];
ll a[maxn];
void floyd() {
    memset(s, 0x3f3f, sizeof(s));
    for(int i = 1; i <= cnt; ++i)
        for(int j = 1; j <= cnt; ++j)
            s[i][j] = d[i][j]; 
    for(int k = 1; k <= cnt; ++k) 
        for(int i = 1; i <= cnt; ++i) 
            for(int j = 1; j <= cnt; ++j) 
                s[i][j] = min(s[i][j], s[i][k] + s[k][j]);
}
int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
    memset(d, 0x3f3f, sizeof(d));
    for(int t = 0; t <= 62; ++t) {
        int tot = 0, x = 0, y = 0;
        for(int i = 1; i <= n; ++i) if(a[i] & (1LL << t)) {
            ++tot;
            if(!x) x = i;
            else if(!y) y = i;
        }
        if(tot >= 3) {
            printf("%d\n", 3);
            return 0;
        }
        if(tot < 2) continue;
        if(!mp[x]) mp[x] = ++cnt;
        if(!mp[y]) mp[y] = ++cnt;
        d[mp[x]][mp[y]] = d[mp[y]][mp[x]] = 1;
//        printf("%d <--> %d\n", mp[x], mp[y]);
    }
    int ans = maxn;
    for(int x = 1; x <= cnt; ++x)
        for(int y = 1; y <= cnt; ++y) if(d[x][y] == 1) {
            d[x][y] = d[y][x] = 0x3f3f3f3f;
            floyd();
            ans = min(ans, s[x][y] + 1);
            d[x][y] = d[y][x] = 1;
        }
    printf("%d\n", ans > n ? -1 : ans);
    return 0;
}
View Code