2021牛客暑期多校訓練營6


2021牛客暑期多校訓練營6

F.Hamburger Steak

  • 題意

給n個漢堡,m個鍋,每個鍋只能同時做一個漢堡,做第i個漢堡需要\(a_i\)的時間,並且每個漢堡最多只能被兩個鍋做,問在盡可能減少平均每口鍋做漢堡的時間的條件下,每個漢堡的方案。

  • 思路

一看,一想,wtf,好難啊,想了半天,在看,emmm,wc,簽到題
每個漢堡無論被幾個鍋做,都是\(a_i\)那么最大的時間就是\(max(a_i , sum / m + (sum % m != 0))\)
然后直接寫就行了。。

code:


int a[N];
void solve(){
    int n,m;
    sc("%lld%lld", &n, &m);
    int sum = 0, maxn = 0;
    for(int i = 1;i <= n;i ++) {
        scanf("%lld", &a[i]);
        sum += a[i];
        maxn = max(maxn,a[i]);
    }
    int sz = max(maxn, sum / m + (sum % m != 0));
    int now = sz;
    int cnt = 1;
    for(int i = 1;i <= n;i ++) {
        if(now >= a[i]) {
            pr("1 %d %lld %lld\n", cnt, sz - now, sz - now + a[i]);
            now -= a[i];
            if(!now) now = sz,cnt ++;
        }else {
            pr("2 %d %lld %lld %d %lld %lld\n", cnt + 1, 0, a[i] - now, cnt, sz - now, sz);
            now = sz - (a[i] - now);
            cnt ++;
//             if(!now) now += sz, cnt ++;
        }
    }
}

H.Hopping Rabbit

  • 題意

一個二維坐標軸,兔子每次跳的距離是d,並且只向上下左右跳,你需要找到兔子的起始坐標,讓他怎么跳都跳不到給出的陷阱中,陷阱為一個矩陣(\(x_i,y_i \to x_j,y_j\))(左下角到右上角)。

  • 思路

掃描線,把空間上所有大小為d * d的矩形合並起來,然后掃一遍,看看有沒有空的就行

code :

struct node {
    int l,r;
};
// sum維護區間最小值
int sum[N << 2], laz[N << 2];
 
void update(int u,int l,int r,int L,int R,int v) {
    if(L > r || l > R) return;
    if(L <= l && R >= r) {
        sum[u] += v;
        laz[u] += v;
        return;
    }
    int mid = l + r >> 1;
    if(L <= mid) update(u << 1, l, mid, L, R, v);
    if(R > mid) update(u << 1 | 1, mid + 1, r, L, R, v);
    sum[u] = min(sum[u << 1], sum[u << 1 | 1]) + laz[u];
    return;
}
 
int query(int u,int l,int r) {
    if(l == r) return l;
    int mid = l + r >> 1;
    if(laz[u] + sum[u << 1] == sum[u]) {
        return query(u << 1,l,mid);
    }else {
        return query(u << 1 | 1,mid + 1,r);
    }
}
 
vector<node> st[N], ed[N];
 
void solve(){
    int n,d;
    cin >> n >> d;
    bool flag = true;
    for(int i = 1;i <= n;i ++) {
        int x1,x2,y1,y2;
        cin >> x1 >> y1 >> x2 >> y2;
		// 精度問題
        x1 += d * INF,y1 += d * INF, y2 += d * INF, x2 += d * INF;
        vector<node> a,b;
        if(x2 - x1 >= d && y2 - y1 >= d) {
            flag = 0;
        }
        if(x2 - x1 >= d) {
            a.pb({0,d - 1});
        }else if((x2 - 1) % d >= x1 % d) {
            a.pb({x1 % d, (x2 - 1) % d});
        }else {
            a.pb({0,(x2 - 1) % d});
            a.pb({x1 % d, d - 1});
        }
 
        if(y2 - y1 >= d) {
            b.pb({0,d-1});
        }else if((y2 - 1) % d >= y1 % d) {
            b.pb({y1 % d, (y2 - 1) % d});
        }else {
            b.pb({0,(y2 - 1) % d});
            b.pb({y1 % d, d - 1});
        }
 
        for(auto sa : a) {
            for(auto sb : b) {
                st[sa.l].pb(sb);
                ed[sa.r + 1].pb(sb);
            }
        }
    }
    if(!flag) {
        cout << "NO" << endl;
        return;
    }
    for(int i = 0;i < d;i ++) {
        // 維護差分
        for(auto a : st[i]) {
            update(1,0,d-1,a.l,a.r,1);
        }
        for(auto b : ed[i]) {
            update(1,0,d-1,b.l,b.r,-1);
        }
        if(sum[1] == 0) {
            cout << "YES" << endl;
            cout << i << ' ' << query(1,0,d-1) << endl;
            return;
        }
    }
    cout << "NO" << endl;
}

I. Intervals on the Ring

  • 題意

給你一個n,m,序列長度為n且(n,1)為相鄰的,(1,3)表示(1,2,3), (3,1)表示(3,4,5,1), 並且給出m個區間,讓你構造一個k個區間,令這k個區間的交集為給出的m個區間的並集。

  • 思路

構造題,考場沒想到簡單的做法,所以寫了賊長
就是類似:
110011001100110011
連續1表示需要交集的部分,那么就這樣構造, (5,2), (9,5),(13,10),(17,14)記錄每一個連續的l,r即可,\(r_i \to l_{i-1}\)即可,最后特判最后一個和第一個是否相連即可。

code:

int a[N];
 
vpi ans;
void solve(){
    int n,m;
    cin >> n >> m;
    int l,r;
    ans.clear();
    for(int i = 1;i <= 2 * n;i ++) a[i] = 0;
    fep(i,1,m) { // 構建差分數組
        cin >> l >> r;
        if(l <= r) {
            a[l] ++, a[r + 1] --;
        }else {
            a[l] ++, a[r + n + 1] --;
        }
    }
    if(m == 1) {
        cout << "1" << endl << l << ' ' << r << endl;
        return;
    }
    for(int i = 1;i <= 2 * n;i ++) {
        a[i] += a[i - 1];
    }
    // 建立雙指針
    l = 1,r = 0;
    while(l <= n && !a[l] && !a[l + n]) l ++;
    r = l;
    while(r <= n && (a[r] || a[r + n])) r ++;
    if(r > n) {
        cout << "1" << endl << l << ' ' << n << endl;
        return;
    }
    int now = r;
    r --;
    ans.pb({l,r});
    while(now <= n && (!a[now] && !a[now + n])) now ++;
    while(now <= n) {
        int i = now;
        while(i <= n && (a[i] || a[i + n])) i ++;
        ans.push_back({now,i - 1});
        now = i;
        while(now <= n && (!a[now] && !a[now + n])) now ++;
    }
    if(ans.size() == 1) {
        cout << "1" << endl;
        cout << ans[0].first << ' ' << ans[0].second << endl;
    }else {
        if((a[n] || a[n + n]) && (a[1] || a[1 + n])) {
            cout << ans.size() - 1 << endl;
            for(int i = 1;i < ans.size();i ++) {
                cout << ans[i].first << ' ' << ans[i - 1].second << endl;
            }
        }else {
            cout << ans.size() << endl;
            for(int i = 1;i < ans.size();i ++) {
                cout << ans[i].first << ' ' << ans[i - 1].second << endl;
            }
            cout << ans[0].first << ' ' << ans[ans.size() - 1].second << endl;
        }
    }
}

J.Defend Your Country

  • 題意

給出一個無向連通圖,每個點的權值為\(w_i\),那么當一個連通塊中的點數為奇數個那么整個聯通塊的價值是-1*權值和,否則是+1 *權值和,讓你通過刪邊來最大化整個圖中的價值

  • 思路
  1. 如果當前整個圖的點都是偶數個,那么答案直接是\(\sum_{1}^{n} w_i\)
  2. 若為奇數個那么一定是將整個圖變成 偶+1奇+偶 最優,那么令中間的奇數點最小即可。
  3. 那么首先,這個奇數點是割點,然后它去的邊必須也是偶數的

code:

int n,m;
ll w[N];
int h[N], e[M], ne[M], idx;
int dfn[N],low[N],times;
int sz[N];

ll res = 1e9 + 7;

void add(int a,int b) {
    e[idx] = b,ne[idx] = h[a], h[a] = idx ++;
}

void tarjan(int u,int fa = -1) {
    dfn[u] = low[u] = ++ times;
    sz[u] = 1;
    bool ok = 1;
    for(int i = h[u];~i;i = ne[i]) {
        int j = e[i];
        if(j == fa) continue;
        if(!dfn[j]) {
            tarjan(j,u);
            low[u] = min(low[u],low[j]);
            if(u == 1) {
                // 根節點是割點的情況
                if(i != h[u] && sz[j] % 2) ok = 0;
            }
            else if(dfn[u] <= low[j] && sz[j] % 2) { // 割點
                ok = 0;
            }
            sz[u] += sz[j];
        }else low[u] = min(low[u], dfn[j]);
    }
    if(ok) res = min(res, w[u]);
}


void solve(){
    n = rd(), m = rd();
    ll sum = 0;
    fep(i,1,n) w[i] = rd(), sum += w[i];
    idx = times = 0;
    res = 1e9 + 7;
    for(int i = 1;i <= n;i ++) {
        h[i] = -1;
        dfn[i] = 0;
        sz[i] = 0;
    }
    fep(i,1,m) {
        int a = rd(),b = rd();
        add(a,b);
        add(b,a);
    }
    if(n % 2 == 0) {
        cout << sum << endl;
        return;
    }
    // 尋找割點,令整個圖分割成(偶奇偶)即可
    tarjan(1);

    ll ans = max(-sum, sum - 2 * res);
    pr("%lld\n", ans);
}


免責聲明!

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



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