German Collegiate Programming Contest 2021 ABCEHIJKLG namomo camp day1 BAIDHGCFE

namomo camp day1

目录

B - Brexiting and Brentering

字符串替换

void solve()
{       
    string s;   cin>>s;
    int n = s.size();
    for(int i = n - 1; i >= 0; i--)
    {
        if(s[i] == 'a' || s[i] == 'e' || s[i] == 'i' ||
            s[i] == 'o' || s[i] == 'u')
        {
            for(int j = 0; j <= i; j++)
                cout<<s[j];
            cout<<"ntry\n";
            return;
        }
    }
    cout<<s<<'\n';
    return;
}

A - Amusement Arcade

对于一段区间将其分成两段去做,只考虑其中一段区间即可,不难发现当区间大小为 (2 ^ a + 1) 时可以满足条件,判断是否满足 (n = 2 ^ a + 2 ^ b + 1) 即可

void solve()
{       
    ll n;
    cin>>n;
    ll b1 = 1;
    if(n == 1 || n == 3)
    {
        cout<<1<<'\n';
        return;
    }
    for(int p = 0; p <= 60; p++)
    {
        if(p) b1 *= 2;
        ll b2 = 1;
        for(int q = 0; q <= 60; q++)
        {
            if(q)   b2 *= 2;
            //cout<<b1 + b2 +<<" "<<b1<<" "<<b2<<'\n';
            if(n == b1 + b2 + 1)
            {
                if(n == 1)
                    cout<<1<<'\n';
                else
                    cout<<b1 + 1<<'\n';
                return;
            }
        }
    }
    cout<<"impossible\n";
    return;
}

I - Monty’s Hall

第一次没选中: (\dfrac{d - s}{d}) ,换 (l) 个选中了 (\dfrac{l}{d - s - e})

第一次选中: (\dfrac{s}{d}) ,换 (l) 个还是选中了 (\dfrac{s - l}{s})

答案: $\dfrac{d - s}{d} \times \(\dfrac{l}{d - s - e}\)+\dfrac{s}{d}$$\times \dfrac{s - l}{s}$ ,(l = \min(s, d - s - e))

void solve()
{       
    int d, s, e;    cin>>d>>s>>e;
    int l = min(s, d - s - e);
    double res = 1.0 * (d - s) / d * l / (d - s - e) + 1.0 * s / d * (s - l) / s;
    //cout<<res<<'\n';
    printf("%.7lf\n", res);
    return;
}

D - Excursion to Porvoo

离线操作,维护区间最小值,再依次加边

用 set 也可以,藕用线段树作,显得我很蠢

const int N = 1e5 + 10;

int n, m, q, cnt[N];  
vector<array<int, 2>> event;
vector<array<ll, 2>> e[N];
ll res[N];
struct segtree
{
    ll s, w, p;
}seg[N * 4];

void update(int id)
{
    seg[id].s = seg[id * 2].s + seg[id * 2 + 1].s;
    seg[id].w =  min(seg[id * 2].w, seg[id * 2 + 1].w);
    if(seg[id * 2].w <= seg[id * 2 + 1].w)
        seg[id].p = seg[id * 2].p;
    else
        seg[id].p = seg[id * 2 + 1].p;
}

void build(int id, int l, int r)
{
    if(l == r)
    {
        seg[id].s = 0, seg[id].w = -1, seg[id].p = l;
        return;
    }
    int mid = (l + r) >> 1;
    build(id * 2, l, mid);
    build(id * 2 + 1, mid + 1, r);
    update(id);
}

void change(int id, int l, int r, int pos, ll s, ll w)
{
    if(l == r)
    {
        seg[id].s = s;
        seg[id].w = w;
        return;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid)
        change(id * 2, l, mid, pos, s, w);
    else 
        change(id * 2 + 1, mid + 1, r, pos, s, w);
    update(id);
}

void solve()
{       
    cin>>n>>m;
    int r = n - 1;
    for(int i = 1; i <= m; i++)
    {
        ll u, s, w; cin>>u>>s>>w;
        e[u].push_back({w, s});
    }
    for(int i = 1; i < n; i++)
        sort(e[i].begin(), e[i].end());
    cin>>q;
    for(int i = 1; i <= q; i++)
    {
        int w;  cin>>w;
        event.push_back({w, i});
    }
    sort(event.begin(), event.end());
    build(1, 1, r);
    //return;
    for(auto [w, id] : event)
    {
        while(seg[1].w < w)
        {
            ll cost = 1e18;
            int pos = seg[1].p;
            for(int i = cnt[pos]; i < e[pos].size(); i++)
                if(e[pos][i][0] >= w && e[pos][i][1] <= cost)
                {
                    cost = e[pos][i][1];
                    cnt[pos] = i + 1;
                }
            // cout<<pos<<"   "<<cost<<'\n';
            if(cost == 1e18)
            {
                break;
            }
            else
            {
                // cout<<pos<<" "<<cnt[pos]<<"  "<<e[pos][cnt[pos] - 1][1]<<"  "<<e[pos][cnt[pos] - 1][0]<<'\n';
                change(1, 1, r, pos, e[pos][cnt[pos] - 1][1], e[pos][cnt[pos] - 1][0]);
                //break;
            }
        }
        // cout<<"calc: "<<w<<"  "<<id<<"  "<<seg[1].w<<"  "<<seg[1].s<<'\n';
        if(seg[1].w >= w)
            res[id] = seg[1].s;
        else
            res[id] = -1;
        //break;
    }
    for(int i = 1; i <= q; i++)
    {
        if(res[i] == -1)    cout<<"impossible\n";
        else
            cout<<res[i]<<'\n';
    }
    return;
}

H - Looking for Waldo

枚举每个位置和一条边的长度,再二分另一条边的长度,时间复杂度(O(h^2w\log w))(O(hw^2\log h))

好像有更快的立方做法

int n, m;  
void solve()
{       
    cin>>n>>m;
    int s[n + 10][m + 10][6];
    memset(s, 0, sizeof s);
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
        {
            char x; cin>>x;
            if(x == 'W')    s[i][j][1] = 1;
            else if(x == 'A') s[i][j][2] = 1;
            else if(x == 'L') s[i][j][3] = 1;
            else if(x == 'D') s[i][j][4] = 1;
            else if(x == 'O') s[i][j][5] = 1;
        }
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            for(int k = 1; k <= 5; k++)
                s[i][j][k] = s[i][j][k] + s[i - 1][j][k] + s[i][j - 1][k] - s[i - 1][j - 1][k];
    // for(int k = 1; k <= 5; k++)
    //     cout<<s[n][m][k]<<'\n';
    int res = n * m + 1;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            for(int x2 = i; x2 <= n; x2++)
            {
                int x = i, y = j;
                int l = 1, r = m - j + 1;
                bool st = true;
                while(l < r)
                {
                    int mid = (l + r) >> 1;
                    int y2 = y + mid - 1;
                    bool ok = true;
                    for(int k = 1; k <= 5; k++)
                    {
                        int t = s[x2][y2][k] - s[x2][y - 1][k] 
                        - s[x - 1][y2][k] + s[x - 1][y - 1][k];
                        //cout<<i<<"   "<<j<<"  "<<k<<"  " <<t<<"  "<<mid<<'\n';
                        if(t == 0)
                            ok = false;
                    }
                    if(ok)  r = mid;
                    else l = mid + 1;
                }
                for(int k = 1; k <= 5; k++)
                {
                    int y2 = y + l - 1;
                    int t = s[x2][y2][k] - s[x2][y - 1][k] 
                    - s[x - 1][y2][k] + s[x - 1][y - 1][k];
                    //cout<<i<<"   "<<j<<"  "<<k<<"  " <<t<<"  "<<'\n';
                    if(t == 0)
                        st = false;
                }
                if(st)
                    res = min((x2 - x + 1) * l, res);                
            }
            //cout<<i<<" "<<j<<" "<<l<<'\n';
    if(res == n * m + 1)
        cout<<"impossible\n";
    else
        cout<<res<<'\n';
    return;
}

G - Killjoys’ Conference

对于每个连通块(哪怕只有一个点),都可以分成两堆,去放进房间,那么设连通块的个数为 (c) ,答案即为 (2 ^ {c - 1} + 1), 这里 (c) 为什么要 -1 手玩一下就懂了

无解情况看样例三分析,存在长度为 (3) 的环就无解

typedef long long ll;

//const int mod = 1e9 + 7;
const int N = 1e6 + 10;
ll qmi(ll a, ll b, ll mod)
{
    ll ans = 1 % mod;
    while(b)
    {
        if(b & 1) ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}
int n, m, p, f[N];  
vector<int> e[N];
bool ok = true;
void dfs(int u, int from)
{
    for(auto v : e[u])
    {
        if(v == from)   continue;
        if(f[v])
        {
            if(f[u] - f[v] == 2)
                ok = false;
        }
        else
        {
            f[v] = f[u] + 1;
            dfs(v, u);
        }
    }
}

void solve()
{       
    cin>>n>>m>>p;
    for(int j = 1; j <= m; j++)
    {
        int u, v;   cin>>u>>v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    ll res = 1;
    int c = 0;
    for(int i = 1; i <= n; i++)
    {
        if(f[i] == 0)
        {
            f[i] = 1;
            dfs(i, 0);
            c++;
        }
    }
    res = res * qmi(2, c - 1, p) % p;
    res = res + 1;
    res %= p;
    if(ok)
        cout<<res<<'\n';
    else
        cout<<"impossible\n";
    return;
}

C - Card Trading

藕英语不好,老婆给我读的题,告诉我只要报价为 (x)(\min(满足 x \leq 买入价格 的次数,满足 x \geq 卖出价格 的次数) \times x)

题目要求输出两位小数,那么关于精度问题,可以做一个让价格乘100的操作,最后输出答案的时候再除回去并特判一下,时间复杂度 (O(N \log N))

typedef long long ll;

const int N = 1e6 + 10;
int n;
array<ll, 3> s[N];
ll sa[N], sb[N];
void solve()
{       
    cin>>n;
    for(int i = 1; i <= n; i++)
    {
        double p;   ll a, b;   
        cin>>p>>a>>b;
        ll pp = p * 100ll;
        s[i] = {pp, a, b};
    }
    sort(s + 1, s + 1 + n);
    for(int i = 1; i <= n; i++)
        sb[i] = sb[i - 1] + s[i][2];
    for(int i = n; i >= 1; i--)
        sa[i] = sa[i + 1] + s[i][1];

    ll r1 = -1, r2 = -1;
    for(int i = 1; i <= n; i++)
    {
        ll t1 = min(sa[i], sb[i]) * s[i][0];
        if(t1 > r2)
            r1 = s[i][0], r2 = t1;
    }
    ll t1 = r1 / 100, t3 = r2 / 100;
    if(r2 != 0)
    {
        cout<<t1<<".";
        ll t2 = r1 % 100;
        if(t2 >= 0 && t2 <= 9)
            cout<<0;
        cout<<t2<<" ";
        cout<<t3<<".";
        t2 = r2 % 100;
        if(t2 >= 0 && t2 <= 9)
            cout<<0;
        cout<<t2<<" ";
    }
    else
        printf("impossible\n");
    return;
}

F - Hectic Harbour II

模拟起来是两个对顶栈,直接去做复杂度是 (O(N^2)) 的,那么我们观察到在拿新的编号 (i) 的时候,只要我们的集装箱在 (i) 的上面或者下面就对答案有 (1) 的贡献,那么我们只需要去维护第 (x) 个是从左往右数第 (y) 个就可以了,考虑用树状数组去维护区间个数,时间复杂度 (O(N \log N))

对于从左往右的定义:

样例2中

6 4 3 
2 4 0 1
6 3 5

看成这样的,就可以去做了

2 4 0 1 5 6 3
const int N = 4e5 + 10;
template<class T>
struct BIT {
    T c[N];
    int size;
    void resize(int s) { size = s;}
    T query(int x) { // 1 ... x
        assert(x <= size);
        T s = 0;
        for (; x; x -= x & (-x)) {
        s += c[x];
    }
    return s;
    }

    void modify(int x, T s) { // a[x] += s
        assert(x != 0);
        for (; x <= size; x += x & (-x)) {
            c[x] += s;
        }
    } 
};

BIT<ll> tr;  
int n;  
int n1, n2;
int pos[N];
void solve()
{       
    cin>>n>>n1>>n2;
    tr.resize(n + 1);
    for(int i = 1; i <= n1; i++)
    {
        tr.modify(i, 1);
        int x;  cin>>x;
        pos[x] = i;
    }
    for(int i = n1 + 1, j = 0; i <= n1 + n2; i++, j++)
    {
        tr.modify(i, 1);
        int x;  cin>>x;
        pos[x] = n1 + n2 - j;
    }
    int p = n1 + 1, res = 0;
    for(int i = 1; i <= n; i++)
    {
        int rp = tr.query(pos[i]);
        int r0 = tr.query(pos[0]);
        if(r0 == rp - 1 || r0 == rp + 1)
            res++;
        tr.modify(pos[i], -1);
    }
    cout<<res<<'\n';
    return;
}

E - Grid Delivery

贪心地去走即可,ak✌直播说的很清楚了

const int N = 2e3 + 10;

int n, m, g[N][N];
void solve()
{       
    cin>>n>>m;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
        {
            char x; cin>>x;
            if(x == 'C')
                g[i][j] = 1;
        }
    set<int> s;
    s.insert(-1);
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
        {
            if(g[i][j] == 0)    continue;
            auto it = prev(s.upper_bound(j));
            if(it == s.begin()) s.insert(j);
            else s.erase(it), s.insert(j);
            // cout<<i<<"  "<<j<<" "<<s.size()<<'\n';
        }        
    cout<<s.size() - 1<<'\n';
    return;
}