• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
magicat
博客园    首页    新随笔    联系   管理    订阅  订阅
German Collegiate Programming Contest 2021 ABCEHIJKLG namomo camp day1 BAIDHGCFE

namomo camp day1

目录
  • namomo camp day1
    • B - Brexiting and Brentering
    • A - Amusement Arcade
    • I - Monty's Hall
    • D - Excursion to Porvoo
    • H - Looking for Waldo
    • G - Killjoys' Conference
    • C - Card Trading
    • F - Hectic Harbour II
    • E - Grid Delivery

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;
}

本文来自博客园,作者:magicat,转载请注明原文链接:https://www.cnblogs.com/magicat/p/17652057.html

posted on 2023-08-23 16:31  magicat  阅读(46)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3