Codeforces Round #707 Div2 A-D


A - Alexey and Train

模擬,沒啥需要講的,按照題目寫就行了。

#include <bits/stdc++.h>
#define Mid (l + r << 1)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
using namespace std;
int read() {
    char c; int num, f = 1;
    while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0';
    while(c = getchar(), isdigit(c)) num = num * 10 + c - '0';
    return f * num;
}
const int N = 309;
int n, a[N], b[N], tim[N];
void work() {
    n = read();
    for(int i = 1; i <= n; i++) {
        a[i] = read();
        b[i] = read();
    }
    for(int i = 1; i <= n; i++) tim[i] = read();
    int now = a[1] + tim[1];
    for(int i = 2; i <= n; i++) {
        if(now + (b[i - 1] - a[i - 1] + 1) / 2 >= b[i - 1]) {
            now = now + (b[i - 1] - a[i - 1] + 1) / 2;
        } else now = b[i - 1];
        now += a[i] - b[i - 1] + tim[i];
    }
    printf("%d\n", now);
}
signed main()
{
    int Case = read();
    while(Case--) work();
    return 0;
}
View Code

 

B - Napoleon Cake

題意:

有一個n層的樓,對於每個\(i∈[1,n]\),有一個\(a_i\)表示從i往下數\(a_i\)個刷上了顏色,問每層樓有沒有顏色。

題解:

當然可以用線段樹,但是有\(O(n)\)的差分做法,而且代碼量少。
因為不是在線詢問,對於每個\(a_i\),在差分數組上打上標記就可以實現區間修改了,最后詢問的時候求一下前綴和就行。

#include <bits/stdc++.h>
#define Mid (l + r << 1)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
using namespace std;
int read() {
    char c; int num, f = 1;
    while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0';
    while(c = getchar(), isdigit(c)) num = num * 10 + c - '0';
    return f * num;
}
const int N = 2e5 + 1009;
int n, a[N], f[N];
void work() {
    n = read();
    for(int i = 0; i <= n; i++) f[i] = 0;
    for(int i = 1; i <= n; i++) {
        a[i] = read();
        f[(i - a[i] + 1) > 0 ? (i - a[i] + 1) : 1] += 1;
        f[i + 1] -= 1;
    }
    for(int i = 1; i <= n; i++) {
        f[i] += f[i - 1];
        if(f[i] > 0) printf("1 ");
        else printf("0 ");
    }
    printf("\n");
}
signed main()
{
    int Case = read();
    while(Case--) work();
    return 0;
}
View Code

 

C - Going Home

題意:

給定一列數\(f\),要求找出四個不一樣的\(a,b,c,d\)使得\(f_a + f_b=f_c + f_d\),如果沒有輸出\(no\)

\(f_i\le 2.5\times 10^6\)

題解:

發現\(f_a + f_b\)是不超過\(5\times 10^6\)的,我們可以開個桶存下所有的\(f_a + f_b\),只要一個桶里面的數量大於4,那么一定有解。

證明:
有四對數,則情況有以下兩種:
\(A\):\((x, y),(x, z), (z, y), (任意)\)這樣子最后一對一定可以跟前面配對。
\(B\):\((x, y),(x, z), (x, p), (x, q)\)雖然這四個不能匹配,但是發現都\(f_x+f_i\)都等於一個數\(t\),也就是說明\(f_y=f_z=f_p=f_q\)這樣就可以取\(f_y+f_z=f_p + f_q\)

於是,最多枚舉\(2\times 10^7\)個數,一定存在答案。
暴力枚舉就行了。

不過我考場上的做法是直接暴力,有可能被卡,謹慎參考以下代碼。

#include <bits/stdc++.h>
#define Mid (l + r << 1)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
using namespace std;
int read() {
    char c; int num, f = 1;
    while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0';
    while(c = getchar(), isdigit(c)) num = num * 10 + c - '0';
    return f * num;
}
const int N = 5e6 + 1009;
int n, a[N];
struct node {
    int x, y;
};
vector<node> f[N];
signed main()
{
    n = read();
    for(int i = 1; i <= n; i++) a[i] = read();
    for(int l = 1; l <= n; l++) {
        for(int i = 1; i + l <= n; i++) {
            int p = a[i] + a[i + l];
            f[p].push_back((node){i, i + l});
            for(int k = 0; k < f[p].size(); k++) {
                if(f[p][k].x == i || f[p][k].y == i) continue;
                if(f[p][k].x == i + l || f[p][k].y == i + l) continue;
                printf("YES\n");
                printf("%d %d %d %d\n", f[p][k].x, f[p][k].y, i, i + l);
                return 0;
            }
        }
    }
    printf("NO\n");
    return 0;
}
View Code

 

D - Two chandeliers

題意:

兩隊人同時循環報數,每次報數的人假如顏色不一樣就計一次貢獻,問第\(k\)次貢獻產生的時候報到了幾。

兩隊個隊伍內部沒有重復顏色。

題解:

看官方Tutorial給的是CRT,但是我沒有用CRT也水過去了。
考場上沒看見顏色各不同結果掛了,要注意審題啊

我們先交換\(n,m\)使得\(m\le n\),這樣之后我們可以考慮假如\(b\)數組第\(1\)個和\(a\)數組第\(i\)個同時開始報數,\(b\)報回到\(1\)產生幾個貢獻。

這是可以\(O(n)\)處理出來的,方法如下。
我們把\(b\)數組里面的數與下標進行映射,這樣可以把\(b\)數組的顏色換成\(0, 1, 2, ..., m - 1\),然后考慮\(a\)數組內部的貢獻。

\(a\)數組的第\(i\)僅僅不會對從\(i - a[i]\)開始的匹配產生貢獻,這樣我們就可以統計出從第\(i\)個位置開始匹配,不能產生貢獻的位置個數,\(m-cnt[i]\)就是產生的貢獻個數。

這樣結束后,我們一步的跨度就是\(m\)了,但是還是會超時,因為可能\(k\)很大,而\(m\)很小。

這時候我們考慮利用最小公倍數去優化枚舉。
兩列數匹配的循環節是最小公倍數,我們只需要計算最小公倍數匹配的貢獻,然后剩下的部分暴力匹配就行了。

那么最小公倍數的匹配時間復雜度是多少呢?
最小公倍數最壞情況下為\(n\times m\),每次跨越\(m\),最多循環\(n\)步就可以求出來了。

接下來的就暴力了。

#include <bits/stdc++.h>
#define int long long
#define Mid (l + r << 1)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
using namespace std;
int read() {
    char c; int num, f = 1;
    while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0';
    while(c = getchar(), isdigit(c)) num = num * 10 + c - '0';
    return f * num;
}
const int N = 5e5 + 1009;
int n, m, k, a[N], b[N], f[N * 2];
int cnt[N], lcm;
int gcd(int a, int b) {return b == 0 ? a : gcd(b, a % b);}
int cal(int k) {
    int q = 0, now = 1, p = 1, tot = 0;
    while(q < k) {
        if(q + cnt[now] >= k)break;
        q += cnt[now];
        now = (now - 1 + m) % n + 1;
        tot += m;
    }
    while(q < k) {
        q += (a[now] != b[p]);
        now = now % n + 1;
        p = p % m + 1;
        tot++;
    }
    return tot;
}
int cal1(int k) {
    int tt = k / m, q = 0;
    for(int i = 1, now = 1; i <= tt; i++) {
        q += cnt[now];
        now = (now - 1 + m) % n + 1;
    }
    return q;
}
signed main()
{
    n = read(); m = read();
    k = read();
    if(n <= m) {
        swap(n, m);
        for(int i = 1; i <= m; i++) b[i] = read();
        for(int i = 1; i <= n; i++) a[i] = read();
    } else {
        for(int i = 1; i <= n; i++) a[i] = read();
        for(int i = 1; i <= m; i++) b[i] = read();
    }
    memset(f, -1, sizeof(f));
    for(int i = 1; i <= m; i++) 
        f[b[i]] = i - 1;
    for(int i = 1; i <= n; i++) {
        if(f[a[i]] != -1) {
            cnt[(i - f[a[i]] - 1 + n) % n + 1]++;
        }
    }
    for(int i = 1; i <= n; i++) 
        cnt[i] = m - cnt[i];
    int p = n / gcd(n, m) * m;
    int tt = cal1(p), ans = 0;
    ans += (k - 1) / tt * p;
    k = (k - 1) % tt + 1;
    ans += cal(k);
    printf("%lld\n", ans);
    return 0;
}
/*
cnt[i]表示從i開始匹配,能有多少個匹配上
*/
View Code

 


免責聲明!

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



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