Codeforces Round #630 (Div. 2)


傳送門

A. Exercising Walk

顯然橫縱坐標我們可以分開考慮。
假設只考慮橫坐標,若\(x_2\not ={x_1}\),那么向左/向右走可以互相抵消,然后只能往一個方向走;若\(x_2=x_1\),那么就不能向左/向右走。
縱坐標同理。
只需要check一下最終位置是否在矩形內即可。
賽場上寫的代碼有些復雜:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/3/31 21:38:57
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
 
void run() {
    int a, b, c, d; cin >> a >> b >> c >> d;
    int x, y; cin >> x >> y;
    int r1, c1, r2, c2; cin >> r1 >> c1 >> r2 >> c2;
    int w = r2 - r1, h = c2 - c1;
    if(w) {
        int t = min(a / w, b / w);
        a -= t * w, b -= t * w;
        int tt = min(a, b);
        a -= tt, b -= tt;          
    }
    if(a > x - r1 || b > r2 - x) {
        cout << "NO" << '\n';
        return;   
    }
    if(h) {
        int t = min(c / h, d / h);
        c -= t * h, d -= t * h;
        int tt = min(c, d);
        c -= tt, d -= tt;          
    }
    if(c > y - c1 || d > c2 - y) {
        cout << "NO" << '\n';
        return;   
    }
    cout << "YES" << '\n';
    return;
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T;
    while(T--) run();
    return 0;
}

B. Composite Coloring

因為\(a_i\leq 1000\)且為合數,容易證明只需要前面\(11\)個質數即可構造出所有的\(a_i\)
染色的話隨便染就行。

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/3/31 22:01:11
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1000 + 5;
 
int n;
int a[N], col[N];
int primes[N], tot;
bool vis[N];
 
void init() {
    for(int i = 2; i < N; i++) {
        if(!vis[i]) primes[++tot] = i;
        for(int j = i * i; j < N; j += i) vis[j] = true;   
    }
}
 
void run() {
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];
    vector <vector <int>> c, p;
    c.resize(12); p.resize(n + 1);
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= 11; j++) {
            if(a[i] % primes[j] == 0) {
                c[j].push_back(i);
                p[i].push_back(j);
            }
        }
    }
    memset(col, -1, sizeof(col));
    for(int i = 1; i <= 11; i++) {
        for(auto it : c[i]) {
            if(col[it] == -1) {
                col[it] = i;
                break;
            }   
        }
    }
    for(int i = 1; i <= n; i++) if(col[i] == -1) col[i] = p[i][0];
    map <int, int> mp; int num = 0;
    for(int i = 1; i <= n; i++) {
        if(!mp[col[i]]) mp[col[i]] = ++num;
        col[i] = mp[col[i]];
    }
    cout << num << '\n';
    for(int i = 1; i <= n; i++) cout << col[i] << " \n"[i == n];
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    init();
    int T; cin >> T;
    while(T--) run();
    return 0;
}

C. K-Complete Word

貪心搞即可。
容易發現將長度為\(n\)的串划分為\(\displaystyle\frac{n}{k}\)個長度為\(k\)的串,並且每個串都相等且都為回文串。
那么對長度為\(k\)的串暴力找代價最小的回文串就行。
詳見代碼:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/3/31 22:23:34
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 2e5 + 5;
 
int n, k;
int cnt[N][26];
char s[N];
 
void run() {
    cin >> n >> k;
    cin >> (s + 1);
    for(int i = 1; i <= n; i++) {
        for(int j = 0; j < 26; j++) {
            cnt[i][j] = 0;
        }
    }
    for(int i = 1; i <= k; i++) {
        for(int j = i; j <= n; j += k) {
            ++cnt[i][s[j] - 'a'];
        }   
    }
    int l = 1, r = k;
    int ans = 0;
    int d = n / k;
    while(l < r) {
        int res = INF;
        for(int i = 0; i < 26; i++) {
            res = min(res, 2 * d - cnt[l][i] - cnt[r][i]);
        }
        ans += res;
        ++l, --r;
    }
    if(l == r) {
        int res = INF;
        for(int i = 0; i < 26; i++) {
            res = min(res, d - cnt[l][i]);
        }   
        ans += res;
    }
    cout << ans << '\n';
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T;
    while(T--) run();
    return 0;
}

D. Walk on Matrix

題意:
假設現在有一個\(n\cdot m\)的矩陣,每個位置的權值為\(a_{i,j}\)。現在從\((1,1)\)出發要到達\((n,n)\),每次只能往下走或者往右走,代價為:當前價值\(x\)\(a_{i,j}\)\(\&\),即\(x\& a_{i,j}\)
現在有個\(dp\)代碼:

顯然這個\(dp\)是不正確的。
現在要求構造一個矩陣,使得\(dp\)跑出來的結果與最大值相差為\(k\)
限制條件為:\(n,m\leq 500,a_{i,j}\leq 3\cdot 10^5,k\leq 10^5\)

思路:

  • 我們考慮誘導\(dp\)結果為\(0\),此時最大值為\(k\),這樣方便構造。
  • 注意到\(k\)\(a_{i,j}\)的取值范圍,我們考慮\(n\geq 2,m\geq 2\)\(k>0\)的情況:現在得到最大的二進制為\(lim\),最大權值為\((lim*2)-1\),那么直接構造\(a_{2,2}=k+lim,a_{1,2}=lim,a_{2,1}=a_{2,3}=a_{3,2}=k\)
  • 按以上構造可以得出\(dp_{2,2}=lim\),但是最終結果為\(0\),因為\(lim\& k=0\)。但最大的答案應該是\(k\)
  • \(n==1||m==1\)\(k=0\)的情況隨便構造即可。

代碼如下:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/3/31 22:49:29
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
 
int k;
int a[4][4];
 
void run() {
    cin >> k;
    if(k == 0) {
        cout << 1 << ' ' << 1 << '\n' << 0 << '\n';
        return;   
    }
    int lim = 1;
    while(lim < 3e5) lim <<= 1;
    lim >>= 2;
    int MAX = (lim << 1) - 1;
    cout << 3 << ' ' << 3 << '\n';
    a[1][1] = MAX;
    a[2][1] = k;
    a[1][2] = lim;
    a[2][2] = lim + k;
    a[2][3] = a[3][2] = (MAX ^ lim);
    a[3][3] = MAX;
    for(int i = 1; i <= 3; i++) {
        for(int j = 1; j <= 3; j++) {
            cout << a[i][j] << ' ';
        }   cout << '\n';
    }
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}

E. Height All the Same

題意:
現有大小為\(n\cdot m\)的矩陣,每個格子上可以任意選擇一個\([L,R]\)的數。現在可以對該矩形執行任意次數以下兩種操作:

  • 選擇一個格子,令\(a_{i,j}+=2\)
  • 選擇兩個相鄰格子,讓他們都加上\(1\)

如果最終能夠使得所有格子上面數值相同,那么就稱該矩陣為完美的。
現在問每個格子選擇的數值在\([L,R]\)范圍內時有多少完美矩陣。

思路:

  • 從每個權值的奇偶性出發:操作\(1\)不會改變奇偶性,操作\(2\)則相當於一個“翻轉”操作:\(0\rightarrow 1,1\rightarrow 0\)
  • 那么問題等價於給定一個\(01\)矩陣,是否存在一種操作方案,使得所有數為\(0\)或者為\(1\)
  • 那么有一個重要的觀察:如果矩形中有偶數個\(0\)或者\(1\),那么此時可以通過操作\(2\)使得所有數都相同。
  • 現在考慮\(n\cdot m\)的奇偶性:
    • \(n\cdot m\)為奇數,那么會發現不論怎么放,至少會存在偶數個數個\(0\)或者\(1\)。此時隨便放即可。
    • \(n\cdot m\)為偶數,\(0,1\)的個數必須都為偶數。若都為奇數,那么操作\(2\)並不會改變\((奇,奇)\)的局面,因為通過操作\(2\)要么不改變\(0,1\)個數,要么使得個數增加/減少\(2\),不改變\((奇,奇)\)的局面。

現在回到這個題,假設\(x\)\([L,R]\)中偶數的個數,\(y\)為奇數的個數。
因為我們選擇的數在\([L,R]\)范圍內,實際上通過思考我們發現只跟奇偶性相關。當\(n\cdot m\)為奇數時,我們隨便放置就行;當\(n\cdot m\)為偶數時,我們必須放置偶數個奇數以及偶數個偶數,那么枚舉放置偶數的個數:\(\displaystyle\sum_{i=0,2,\cdots,n\cdot m}x^iy^{n\cdot m-i}{nm\choose i}\)
我們容易發現這跟牛頓二項式定理有關。賽場上腦袋短路,忘記如何求解這個式子。。
其實這個式子就等於\(\displaystyle ((x+y)^{n\cdot m}+(x-y)^{n\cdot m}) / 2\)
這貌似是數學中常用的一個技巧。
代碼如下:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/3/31 23:52:41
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5, MOD = 998244353, inv2 = (MOD + 1) >> 1;
int qpow(ll a, ll b) {
    ll res = 1;
    a %= MOD;
    while(b) {
        if(b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;   
    }
    return res;   
}
 
 
void run() {
    ll n, m, L, R;
    cin >> n >> m >> L >> R;
    int even = R / 2, odd = (R + 1) / 2;
    even -= (L - 1) / 2, odd -= L / 2;
    int res = qpow(even + odd, n * m);
    if((n * m) & 1) cout << res << '\n';
    else {
        res += qpow(max(even, odd) - min(even, odd), n * m);
        res %= MOD;
        res = 1ll * res * inv2 % MOD;
        cout << res << '\n';
    }
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}

賽場上貌似很多人直接矩陣快速冪過的,其實這個很好想,根據上面的思考,我們得出最后的結果只與放置奇數個奇數/偶數和放置偶數個奇數/偶數有關。
\(dp[i][2]\)表示前\(i\)個數,放置了奇數個/偶數個奇數/偶數,不妨考慮放置的是奇數。那么轉移為:

  • \(dp[i][1]=dp[i-1][0]\cdot O+dp[i-1][0]\cdot E\);
  • \(dp[i][0]=dp[i-1][1]\cdot O+dp[i-1][0]\cdot E\)

那么當\(n\cdot m\)為奇數時,答案為\(dp[n\cdot m][0]+dp[n\cdot m][1]\);否則,答案為\(dp[n\cdot m][0]\)
這個過程我們用矩陣快速冪進行優化即可。
代碼略。

F. Independent Set

題意:
給出一顆樹\(G=(V,E)\),現在要求

\[\sum_{E'\not ={\emptyset}\subset E}w(G'[E']) \]

其中\(G'[E']\)為圖\(G\)關於邊集\(E'\)的邊生成子圖,\(w(G'[E'])\)是指該圖獨立集的個數。

思路:

  • 考慮對每個結點進行染色,\(dp\)時枚舉每條邊是否斷開。顯然可以分情況討論:

    • \(u\)不染色時,那么邊\((u,v)\)無論是否斷開,都可以從\(v\)染色或者不染色兩種情況進行轉移。
    • \(u\)染色時,若邊\((u,v)\)斷開,那么\(v\)可染色也可不染色;邊\((u,v)\)沒有斷開,那么\(v\)不能染色。
  • 因為要從邊生成子圖中找獨立集,也就是每個染色的點至少會有一條邊與之相連。那么上述兩種情況斷開時兒子結點都可以染色,但要排除兒子結點為單獨一個點的情況。

  • \(dp[u][2]\)\(u\)結點為根節點時,所有兒子結點都沒有邊與之相連的情況。顯然這種情況會被算入\(dp[u][1],dp[u][0]\)

  • 那么我們只需要在上述轉移中將\(dp[v][2]\)\(dp[v][1]\)中減去即可。

這題主要就是要注意一個單獨的點不能染色的情況,而這種情況是容易計算且含於其它\(dp\)狀態中的,所以轉移時直接排除(減去)這種情況即可。
具體轉移細節見代碼:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2020/4/1 20:29:05
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#include <assert.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
  template <template<typename...> class T, typename t, typename... A> 
  void err(const T <t> &arg, const A&... args) {
  for (auto &v : arg) std::cout << v << ' '; err(args...); }
#else
  #define dbg(...)
#endif
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 3e5 + 5, MOD = 998244353;

int n;
vector <int> G[N];
int dp[N][3];

void dfs(int u, int fa) {
    dp[u][0] = dp[u][1] = dp[u][2] = 1;
    for(auto v : G[u]) if(v != fa) {
        dfs(v, u);
        dp[u][0] = 1ll * dp[u][0] * (2ll * dp[v][0] + 2 * dp[v][1] - dp[v][2] + MOD) % MOD;
        dp[u][1] = 1ll * dp[u][1] * (2ll * dp[v][0] + dp[v][1] - dp[v][2] + MOD) % MOD;
        dp[u][2] = 1ll * dp[u][2] * (1ll * dp[v][0] + dp[v][1] - dp[v][2] + MOD) % MOD;
    }   
}

void run() {
    cin >> n;
    for(int i = 1; i < n; i++) {
        int u, v; cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);   
    }
    dfs(1, 0);
    int ans = ((ll)dp[1][0] + dp[1][1] - dp[1][2] + MOD - 1) % MOD;
    cout << ans << '\n';
}   

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM