NWERC 2015(2020.7.1訓練賽)

比賽網址
寫這個主要是來監督自己補題的emmm

總結

比賽我從中間開始看,wlh從頭開始看,cjl從後面開始看。開場我看了E一分鐘不到就開始寫了,在12min的時候交了一發wa了,沒有仔細考慮情況。交完以後發現I / J被瘋狂簽到,wlh在32min時簽下J題,50min時我簽下I題,I差不多時個模擬題,67min時cjl簽下A題,在此期間討論了一下E題,最後想到了一個二分圖匹配的辦法,把板子改了改過了在98min時寫掉E題。今天前期挺好的,此時兩個學長也把K題討論出來了,但是一直wa10這個點,趁他們寫K的時候我去寫了G,大概想了一個三指針的方法但是wa了。中期一道題都沒過,兩個學長被K題卡死,然後我也來寫K題,讀懂題意覺得學長的思路沒有問題,照着這個思路又寫了一遍,結果還是wa。然後學長去看D題,我再那邊死磕K題,在247min時把學長的代碼改了改改對了,在一個小地方的邊界處理不完善。本來應該早早在120minAC的題目拖到這麼久,也導致了整個隊伍的節奏打亂了。同時也學了一波__int128的寫法。

I Identifying Map Tiles

題目描述
根據題目所給的字符串所相對應的圖表,輸出它的地圖的level和它所對應的座標位置
思路
挺好解決的,字符串長度就是圖表level,後面的座標具體位置也很好理解,每次都是由一個格子話分成四個格子,所以對於字符串每個字符的劃分都可以確定這個字符串所對應的座標位置,然後模擬一下即可
代碼

#include<bits/stdc++.h>
using namespace std;

void solve() {
    string s;
    cin >> s;
    printf("%d ", s.size());
    int xl = 1, xr = pow(2, s.size()), yl = 1, yr = pow(2, s.size());
    for(int i = 0; i < s.size(); i++) {
        int x = xl + xr >> 1;
        int y = yl + yr >> 1;
        if(s[i] == '0') {
            xr = x;
            yr = y;
        }
        else if(s[i] == '1') {
            xl = x + 1;
            yr = y;
        }
        else if(s[i] == '2') {
            xr = x;
            yl = y + 1;
        }
        else if(s[i] == '3') {
            xl = x + 1;
            yl = y + 1;
        }
        //printf("%d %d %d %d\n", xl, xr, yl, yr);
    }
    printf("%d %d\n", xr - 1, yr - 1);
}

int main() {
    //freopen("in.txt", "r", stdin);
    solve();
    return 0;
}

E Elementary Math

題目描述
給定n對數字,每對數字可以進行 ‘+’, ‘-’, ’ * ’ 三種操作,最後判斷是否有n個不相同的答案,若有答案,則輸出所對應的解,沒有n個解就輸出impossible。
思路
把題目轉化成二分圖匹配來看,左邊n爲個對數,右邊爲所有可能的解,然後一一匹配即可,差不多就變成了二分圖匹配的裸題
代碼

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef pair<LL, LL> PII;
const int N = 1e4 + 10;
struct Edge {
    int to, next;
}e[N];
int head[N], idx;
bool st[N];
int match[N];
map<LL, int> pos;
vector<PII> cal;
LL tt[N];
int Map[N];

void add(int u, int v) {
    e[++idx].to = v;
    e[idx].next = head[u];
    head[u] = idx;
}

bool find(int u) {
    for(int i = head[u]; i; i = e[i].next) {
        int v = e[i].to;
        if(!st[v]) {
            st[v] = 1;
            if(!match[v] || find(match[v])) {
                match[v] = u;
                Map[u] = v;
                return true;
            }
        }
    }
    return false;
}

void solve() {
    int n;
    scanf("%d", &n);
    int x = 0;
    for(int i = 1; i <= n; i++) {
        LL a, b;
        scanf("%lld%lld", &a, &b);
        if(!pos[a + b]) pos[a + b] = ++x, tt[x] = a + b;
        if(!pos[a * b]) pos[a * b] = ++x, tt[x] = a * b;
        if(!pos[a - b]) pos[a - b] = ++x, tt[x] = a - b;
        add(i, pos[a + b]);
        add(i, pos[a * b]);
        add(i, pos[a - b]);
        cal.push_back({a, b});
    }

    vector<PII>::iterator it;
    x = 0;
    int res = 0;
    for(int i = 1; i <= n; i++) {
        memset(st, 0, sizeof st);
        if(find(i)) res++;
    }

    if(res < n) puts("impossible");
    else {
        for(int i = 1; i <= n; i++) {
            LL num = tt[Map[i]];
            LL a = cal[i - 1].first, b = cal[i - 1].second;
            //printf("%lld %lld %d\n", a, b, match[i]);
            if(a + b == num) printf("%lld + %lld = %lld\n", a, b, num);
            else if(a * b == num) printf("%lld * %lld = %lld\n", a, b, num);
            else if(a - b == num) printf("%lld - %lld = %lld\n", a, b, num);
        }
    }
}

int main() {
    //freopen("in.txt", "r", stdin);
    solve();
    return 0;
}

K Kitchen Combinatorics

題目描述
有r種食材,有三種類型的菜,每種類型的菜可能有不同種食材,同時有n個限制條件,最後問有多少種搭配方式。
思路
根據題目暴力模擬就行了,感覺就被越界範圍給坑了一下吧,乘法判斷以後都先除一下再進行操作了。或者使用__int128了
代碼

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef tuple<int, int, int> TPL;
typedef pair<int, int> PII;
const int N = 1010, M = 110, W = 20010;
LL A[N];
vector<int> e[M];
bool st[M][M];
int u[W], v[W], w[W];
map<set<int>, int> vis;

bool check(int u, int v, int w) {
    if(st[u][v] || st[u][w] || st[v][w]) return false;
    return true;
}

void solve() {
    int r, s, m, d, n;
    scanf("%d%d%d%d%d", &r, &s, &m, &d, &n);
    for(int i = 1; i <= r; i++) {
        scanf("%lld", &A[i]);
    }
    for(int i = 1; i <= s + m + d; i++) {
        int k;
        scanf("%d", &k);
        while(k--) {
            int x;
            scanf("%d", &x);
            e[i].push_back(x);
        }
    }

    for(int i = 1; i <= n; i++) {
        int u, v;
        scanf("%d%d", &u, &v);
        st[u][v] = 1;
        st[v][u] = 1;
    }

    int x = 0;
    for(int i = 1; i <= s; i++) {
        for(int j = s + 1; j <= s + m; j++) {
            for(int k = s + m + 1; k <= s + m + d; k++) {
                if(check(i, j, k)) {
                    ++x;
                    u[x] = i;
                    v[x] = j;
                    w[x] = k;
                }
            }
        }
    }

    LL res = 0;
    set<int>::iterator it;
    for(int i = 1; i <= x; i++) {
        int a = u[i], b = v[i], c = w[i];
        set<int> s;
        for(int j = 0; j < e[a].size(); j++) {
            s.insert(e[a][j]);
        }
        for(int j = 0; j < e[b].size(); j++) {
            s.insert(e[b][j]);
        }
        for(int j = 0; j < e[c].size(); j++) {
            s.insert(e[c][j]);
        }
        LL cnt = 1;

        for(it = s.begin(); it != s.end(); it++) {
            int num = *it;
            if(cnt > (LL)1e18 / A[num]) {
                puts("too many");
                return;
            }
            cnt *= A[num];
        }

        if(res + cnt > (LL)1e18 || res < 0) {
            puts("too many");
            return;
        }
        res += cnt;
    }
    printf("%lld\n", res);
}

int main() {
    //freopen("in.txt", "r", stdin);
    solve();
    return 0;
}

J Jumbled Communication

題目描述
給定一個數字num, 求滿足式子 num = x ^ (x << 1) 的x
思路
x 和 x <<= 1 斜對角數字一定相等,按照num模擬一下即可
代碼

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
vector<int> s1, s2;

void solve() {
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
        int num;
        scanf("%d", &num);
        if(num == 0) {
            printf("0 ");
            continue;
        }
        int x = 0, y = 1;
        s2.push_back(0);
        int k = 8;
        while(k--) {
            if(num & 1) {
                if(s2[x] == 1) s1.push_back(0);
                else if(s2[x] == 0) s1.push_back(1);
                s2.push_back(s1[x]);
            }
            else {
                s2.push_back(s2[x]);
                s1.push_back(s2[x]);
            }
            x++; y++;
            num >>= 1;
        }
        int tmp = 1, res = 0;
        for(int j = 0; j < s1.size(); j++) {
            if(s1[j] == 1) res += tmp;
            tmp <<= 1;
        }
        printf("%d ", res);
        s1.clear(); s2.clear();
    }
}

int main() {
	//freopen("in.txt", "r", stdin);
	solve();
	return 0;
}

A - Assigning Workstations

題目大意
給定n個人的開始時間和工作時間長度,並給m分鐘的機器待機時間(超過m分鐘機器就關機,再工作就要重新開機)。問有多少個人不用重新開機
思路
貪心+優先隊列,先排序每次開始工作的時間,然後將結束時間存到優先隊列裏面,每次判斷隊頂的元素是否符合要求就行了
如果出現wa14的情況,就可能和我一開始思考的一樣了,用一個數組維護二分大小,但是此時存入的結束工作的值的大小不確定,不能維護一個穩定的遞增或遞減序列
代碼

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N = 3e5 + 10;
struct node {
    LL x, y;
    friend bool operator < (node a, node b) {
        if(a.x == b.x) {
            return a.y < b.y;
        }
        return a.x < b.x;
    }
}a[N];
priority_queue<LL, vector<LL>, greater<LL> > q;

void solve() {
    LL n, m;
    scanf("%lld%lld", &n, &m);
    for(int i = 1; i <= n; i++) {
        scanf("%lld%lld", &a[i].x, &a[i].y);
        a[i].y += a[i].x;
    }
    sort(a + 1, a + 1 + n);
    int res = 0;
    for(int i = 1; i <= n; i++) {
        int start = a[i].x, stop = a[i].y;
        while(!q.empty() && q.top() <= start) {
            if(q.top() + m < start) q.pop();
            else {
                q.pop();
                res++;
                break;
            }
        }
        q.push(stop);
    }
    printf("%d\n", res);
}

int main() {
    //freopen("in.txt", "r", stdin);
    solve();
    return 0;
}

D.Debugging

題目描述
有n行代碼,裏面有一個錯誤,編譯一次需要花費r分鐘,printf一行需要花費p分鐘,問最少需要幾分鐘就可以鎖定這個錯誤
思路
記憶化搜索。dp[n]表示代碼數爲n行時所需要解決問題的最少代價。dp[n] = min( x * p + dp[ ceil(n / (x + 1))] + r) (x爲在n行中輸出的printf數量,1 <= x < n)
代碼

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N = 1e6 + 10;
const LL inf = 1e18;
LL n, r, p;
LL dp[N];

LL cacl(LL n) {
    if(n == 1) return 0;
    if(dp[n]) return dp[n];
    LL res = (n - 1) * p + r;
    for(int i = 2; i <= n; i++) {
        res = min(res, cacl(n / i + (n % i != 0)) + (i - 1) * p + r);
    }
    return dp[n] = res;
}

void solve() {
    scanf("%lld%lld%lld", &n, &r, &p);
    cacl(n);
    printf("%lld\n", dp[n]);
}

int main() {
    //freopen("in.txt", "r", stdin);
    solve();
    return 0;
}

G.Guessing Camels(cdq分治,待學待補)

C.Cleaning Pipes

題目描述
給定w個點,再給定n條線,兩線可能相交(同一起點不算相交),判斷從其中一條直線的入口進入是否會和另外一條從其入口進入發生碰撞
思路
計算幾何+二分圖判斷,如果線A和線B有交點,要麼從A進入,要麼從B進入。如果選擇A線,則B線上若再有進入就會發生碰撞,所以一旦出現了必須由從B線進入的情況。就impossible。可以用構建圖以後判定二分圖
(判定兩直線是否相交是從某博客上偷來的板子emmm)
代碼

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 1005;
struct Point {
    int x, y;
}w[N], p[N];
int st[N];

// 判斷兩線段是否相交
int direction (Point p0, Point p1, Point p2) {
    return ((p2.x - p0.x)*(p1.y - p0.y) - (p1.x - p0.x)*(p2.y - p0.y));
}

bool on_segment (Point p0, Point p1, Point p2) {
    int minx, maxx, miny, maxy;
    minx = min(p0.x, p1.x);
    maxx = max(p0.x, p1.x);
    miny = min(p0.y, p1.y);
    maxy = max(p0.y, p1.y);
    if (p2.x >= minx && p2.x <= maxx && p2.y >= miny && p2.y <= maxy)
        return true;
    else
        return false;
}

bool segments_intersect (Point p1, Point p2, Point p3, Point p4) {
    int d1, d2, d3, d4;
    d1 = direction(p3, p4, p1);
    d2 = direction(p3, p4, p2);
    d3 = direction(p1, p2, p3);
    d4 = direction(p1, p2, p4);
    if (((d1 < 0 && d2 > 0) || (d1 > 0 && d2 < 0)) && ((d3 < 0 && d4 > 0) || (d3 > 0 && d4 < 0)))
        return true;
    else if (d1 == 0 && on_segment(p3, p4, p1))
        return true;
    else if (d2 == 0 && on_segment(p3, p4, p2))
        return true;
    else if (d3 == 0 && on_segment(p1, p2, p3))
        return true;
    else if (d4 == 0 && on_segment(p1, p2, p4))
        return true;
    else
        return false;
}

// 染色法判定二分圖
vector<int> e[N];
bool flag;
int color[N];

void dfs(int u, int st) {
    for(int i = 0; i < e[u].size(); i++) {
        int v = e[u][i];
        if(!color[v]) {
            color[v] = -st;
            dfs(v, -st);
        }
        else {
            if(color[v] == st) flag = 0;
        }
    }
}

void solve() {
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++) {
        scanf("%d%d", &w[i].x, &w[i].y);
    }
    for(int i = 1; i <= m; i++) {
        scanf("%d%d%d", &st[i], &p[i].x, &p[i].y);
    }

    for(int i = 1; i <= m; i++) {
        for(int j = i + 1; j <= m; j++) {
            if(st[i] == st[j]) continue;
            if(segments_intersect(w[st[i]], p[i], w[st[j]], p[j])) {
                e[i].push_back(j);
                e[j].push_back(i);
            }
        }
    }

    flag = 1;
    for(int i = 1; i <= m; i++) {
        if(!color[i]) {
            color[i] = 1;
            dfs(i, 1);
        }
    }
    if(flag) puts("possible");
    else puts("impossible");

}

int main() {
    //freopen("in.txt", "r", stdin);
    solve();
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章