• 博客园logo
  • 会员
  • 周边
  • 众包
  • 新闻
  • 博问
  • 闪存
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
magicat
博客园    首页    新随笔    联系   管理    订阅  订阅
2023/8/9~2023/8/11 做题

2023/8/9~2023/8/11 做题

目录
  • 2023/8/9~2023/8/11 做题
    • Codeforces Round 121 (Div. 1) C. Fools and Roads
    • Codeforces Round 343 (Div. 2) C. Famil Door and Brackets
    • Codeforces Round 880 (Div. 1) A. k-th equality
    • Codeforces Round 867 (Div. 3) G1. Magic Triples (Easy Version)
    • Codeforces Round 567 (Div. 2)D. Irrigation
    • Mail.Ru Cup 2018 Round 3 E. Check Transcription
    • RCC 2014 Warmup (Div. 2) D. Cunning Gena
    • Codeforces Round 867 (Div. 3) F. Gardening Friends
    • Codeforces Round 296 (Div. 2) D. Clique Problem
    • Codeforces Beta Round 52 (Div. 2) E. Domino Principle
    • Codeforces Round 212 (Div. 2) C. Insertion Sort
    • VK Cup 2015 - Round 2 (unofficial online mirror, Div. 1 only) B. Work Group
    • Codeforces Round 874 (Div. 3) F. Ira and Flamenco
    • Codeforces Round 455 (Div. 2) D - Colorful Points
    • Codeforces Round 870 (Div. 2) D. Running Miles
    • Codeforces Round 179 (Div. 2) B. Yaroslav and Two Strings

Codeforces Round 121 (Div. 1) C. Fools and Roads

树形dp + LCA

先预处理LCA,将边下放到点处理, 对于每个路径在其 \(u\) , \(v\), \(lca\) 处分别打个标记,树形dp合并即可

const int N = 2e5 + 10;
const int LOGN = 20;

int n;
vector<int> e[N];
int id[N], cnt, dep[N], fa[N][LOGN + 2], val[N];
int res[N];
map<pair<int, int>, int> mp;

void lca_init()
{
    for(int j = 1; j <= LOGN; j++)
        for(int i = 1; i <= n; i++)
            fa[i][j] = fa[fa[i][j - 1]][j - 1];
}
int lca_query(int u, int v)
{
    if(dep[u] < dep[v])   swap(u, v);
    int d = dep[u] - dep[v];
    for(int j = LOGN; j >= 0; j--)
        if(d & (1 << j))
            u = fa[u][j];
    if(u == v)    return u;
    for(int j = LOGN; j >= 0; j--)
        if(fa[u][j] != fa[v][j])
            u = fa[u][j],v = fa[v][j];
    return fa[u][0];
}

void dfs(int u, int from)
{
    dep[u] += dep[from] + 1;
    for(auto v : e[u])
    {
        if(v == from)   continue;
        id[v] = mp[{u, v}];
        fa[v][0] = u;
        dfs(v, u);
    }
}

void dfs2(int u, int from)
{
    for(auto v : e[u])
    {
        if(v == from)   continue;
        dfs2(v, u);
        val[u] = val[u] + val[v];
    }
    res[id[u]] = val[u];
}

void solve()
{       
    cin>>n;
    for(int i = 1; i < n; i++)
    {
        int u, v;   cin>>u>>v;
        mp[{u, v}] = ++cnt;
        mp[{v, u}] = cnt;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    dfs(1, 0);
    lca_init();
    int q;  cin>>q;
    while(q--)
    {
        int u, v;   cin>>u>>v;
        int lca = lca_query(u, v);
        val[u]++, val[v]++, val[lca] -= 2;
    }

    // for(int i = 1; i <= n; i++) 
    //     cout<<id[i]<<"  ";
    // cout<<'\n';
    // for(int i = 1; i <= n; i++) 
    //     cout<<val[i]<<"  ";
    // cout<<'\n';

    dfs2(1, 0);
    for(int i = 1; i < n; i++)
        cout<<res[i]<<" ";
    cout<<'\n';
    return;
}

Codeforces Round 343 (Div. 2) C. Famil Door and Brackets

dp

定义状态 \(f_{i,j}\) ,为有 \(i\) 长度的字符串,其 \(j\) 为 \((\) 的数量 - \()\) 的数量,其中找出 \(S\) 串中 \((\) 的数量 - \()\) 的最小值 \(miv\), 其 \(P\) 的\((\) 的数量 - \()\) 的数量 \(\geq miv\),其 \(Q\) 中受到 \(P\) 和 \(S\) 的限制,\((\) 的数量 \(\geq )\) 的数量,我们只要倒着算答案就行

\[f_{i,j} = f_{i - 1, j - 1} + f_{i - 1, j + 1} \]

\[res = f_{len, i} \times f_{n - m - len, i + \texttt{S 中 '('数量 - ')' 的数量}} \]

其中满足 \(i + \texttt{S 中 前缀'('数量 - 前缀')' 的最小值} \geq 0 \&\& i + \texttt{S 中 '('数量 - ')' 的数量} \leq n - m - len\) 的条件

int n, m;
int f[N][N], g[N][N];
string s;
void solve()
{       
    cin>>n>>m>>s;
    int cnt = 0, miv = 1e9;
    for(auto it : s)
    {
        if(it == '(')   cnt++;
        else cnt--;
        miv = min(cnt, miv);
    }
    f[0][0] = 1;
    for(int i = 1; i <= N - 10; i++)
    {
        f[i][0] = f[i - 1][1];
        for(int j = 1; j <= i; j++)
            f[i][j] = (f[i - 1][j - 1] + f[i - 1][j + 1]) % mod;
    }
    ll res = 0;
    for(int len = 0; len <= n - m; len++)
    {
        for(int i = 0; i <= len; i++)
        {
            if(i + miv >= 0 && i + cnt <= n - m - len)
            {
                //cout<<len<<"    "<<i<<"    "<<f[len][i]<<"   "<<f[n - m - len][i + cnt]<<'\n';
                res = (res + 1ll * f[len][i] * f[n - m - len][i + cnt]) % mod;
            }
        }
    }
    cout<<res<<'\n';
    return;
}

Codeforces Round 880 (Div. 1) A. k-th equality

在数位要求下,找到等式的第 \(k\) 小字典序

直接跑暴力

注意到题面 Each input file has at most $ 5 $ test cases which do not satisfy $ A, B, C \leq 3 $ .就是我们可以跑暴力的依据

穷举数字 \(a\) ,根据 \(c\) 的上下界确定 \(b\) 的上下界即可

ll qpow(ll base, ll r)
{
    int res = 1;
    while(r--)
        res = res * base;
    return res;
}

void solve()
{       
    ll a, b, c, k; cin>>a>>b>>c>>k;
    ll down1 = qpow(10, a - 1), up1 = qpow(10, a) - 1;
    ll down2 = qpow(10, b - 1), up2 = qpow(10, b) - 1;
    ll down3 = qpow(10, c - 1), up3 = qpow(10, c) - 1;
    ll d = 0;
    for(ll i = down1; i <= up1; i++)
    {
        ll miv = down3 - i;
        ll mav = up3 - i;
        ll l1 = max(miv, down2);
        ll r1 = min(mav, up2);
        d += max(0ll, (r1 - l1 + 1));
        if(d >= k)
        {
            d -= (r1 - l1 + 1);
            d = k - d;
            cout<<i<<" + "<<l1 + d - 1<<" = "<<i + l1 + d - 1<<'\n';
            return;
        }
    }   
    cout<<-1<<'\n';
    return;
}

Codeforces Round 867 (Div. 3) G1. Magic Triples (Easy Version)

对于一个数,他的上 \(a \times b \times b \leq 1000000\) 其 \(1 \leq b \leq 1000\) 次

对于\(1 \leq i \leq 200000\) 对于每个 \(i\) 去做枚举 \(b\) 这个事情, 总共可以枚举到的 \(b\) 的数量只有 \(793494\) 个,所以枚举就行

ll n, f[N];
void solve()
{       
    cin>>n;
    vector<ll> a;
    for(int i = 1; i <= n; i++)
    {
        ll x;   cin>>x;
        a.push_back(x);
        f[x]++;
    }
    sort(a.begin(), a.end());
    a.erase(unique(a.begin(), a.end()), a.end());

    ll res = 0;
    for(auto it : a)
    {
        for(int j = 1; it * j * j <= 1000000; j++)
        {
            if(j == 1 && f[it] >= 3)
                res = res + f[it] * (f[it] - 1) * (f[it] - 2);
            else if(j != 1)
                res = res + f[it] * f[it * j] * f[it * j * j];
        }
        f[it] = 0;
    }
    cout<<res<<'\n';
    return;
}

Codeforces Round 567 (Div. 2)D. Irrigation

考虑离线,线段树维护最小编号城市,线段树二分查找答案

先将最小举办次数的城市编号在线段树上赋值,每次给最小举办城市编号的举办次数 + 1,同时若有询问 \(k \leq \texttt{历史举办次数 + 最小举办城市数量}\),就在线段树上二分查找,然后将新成为最小举办城市编号的城市在线段树上赋值。

为什么这样模拟可以,前 \(n\) 年的数量很少,这样最多只有\(\sqrt{n}\) 次模拟,所有城市的举办次数就都一样。然后直接取模算答案

具体见代码

const int N = 5e5 + 10;

ll n, m, k;
ll a[N], res[N];
vector<ll> vx;

struct segtree
{
    int val;     
}seg[N * 4];

void update(int id)
{
    seg[id].val = seg[id * 2].val + seg[id * 2 + 1].val;
}

void change(int id, int l, int r, int pos)
{
    //cout<<l<<'\n';

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

int query(int id, int l, int r, int k)
{
    if(l == r)
    {
        return l;
    }
    int mid = (l + r) >> 1;
    if(seg[id * 2].val >= k)
        return query(id * 2, l, mid, k);    
    else 
        return query(id * 2 + 1, mid + 1, r, k - seg[id * 2].val);
}

void solve()
{       
    cin>>n>>m>>k;
    for(int i = 1; i <= n; i++)
    {
        ll x;  cin>>x;
        a[x]++;
    }
    vector<pair<ll, ll>> b, event;
    vector<int> c;
    ll miv = 1e18;
    for(int i = 1; i <= k; i++)
    {
        ll x;   cin>>x; x-= n;
        event.push_back({x, i});
    }
    for(int i = 1; i <= m; i++)
    {
        if(a[i] < miv)
        {
            c.clear();
            miv = a[i];
        }
        if(a[i] == miv)
            c.push_back(i);
    }
    for(int i = 1; i <= m; i++)
        if(a[i] != miv)
            b.push_back({a[i], i});

    sort(b.begin(), b.end());
    sort(event.begin(), event.end());
    for(auto it : c)
        change(1, 1, m, it);

    ll last = 0, cnt = 0, cnt2 = 0;
    while(cnt2 < b.size() && cnt < k)
    {
        //cout<<seg[1].val<<"   "<<cnt<<"   "<<cnt2<< '\n';
        ll add = c.size();
        while(cnt < k && last + add >= event[cnt].first)
        {   
            res[event[cnt].second] = query(1, 1, m, event[cnt].first - last);
            cnt++;
        }
        last += add;
        miv++;
        while(cnt2 < b.size() && b[cnt2].first == miv)
        {
            change(1, 1, m, b[cnt2].second);
            c.push_back(b[cnt2].second);
            cnt2++;
        }
        //cout<<seg[1].val<<'\n';
        //cout<<cnt<<" "<<cnt2<<'\n';
    }
    for(; cnt < k; cnt++)
    {
        //res[event[cnt].second] = (event[cnt].first - last) % m;
        if((event[cnt].first - last) % m == 0)
            res[event[cnt].second] = m;
        else
            res[event[cnt].second] = (event[cnt].first - last) % m;
    }
    for(int i = 1; i <= k; i++)
        cout<<res[i]<<'\n';
    return;
}

Mail.Ru Cup 2018 Round 3 E. Check Transcription

哈希

哈希处理 \(t\) ,直接枚举\(r_0\)的长度,可得出 \(r_1\)的长度,根据其长度哈希判断即可

为什么不会 \(TLE\) 呢?

见此处 题解 CF1056E 【Check Transcription】

注意特判

typedef pair<long long, long long> pll;
struct DoubleStringHash
{
    vector<long long> h1, h2, w1, w2;
    long long base1 = 131, base2 = 13331;
    long long p1 = 1e9 + 7, p2 = 1e9 + 9;
    void init(string s) {
        int len = s.size();
        s = " " + s;
        h1.resize(len + 1), w1.resize(len + 1);
        h2.resize(len + 1), w2.resize(len + 1);
        h1[0] = 0, w1[0] = 1;
        h2[0] = 0, w2[0] = 1;
        for(int i = 1; i <= len; i++) {
            h1[i] = (h1[i - 1] * base1 + s[i]) % p1, w1[i] = w1[i - 1] * base1 % p1;
            h2[i] = (h2[i - 1] * base2 + s[i]) % p2, w2[i] = w2[i - 1] * base2 % p2;
        }
    }
    pll get(int l, int r) {
        return {(h1[r] - h1[l - 1] * w1[r - l + 1] % p1 + p1) % p1, (h2[r] - h2[l - 1] * w2[r - l + 1] % p2 + p2) % p2};
    }
    bool same(pll a, pll b)
    {
        if(a.first == b.first && a.second == b.second)
            return true;
        else 
            return false;
    }
};
string t, s;
int z, o;
void solve()
{       
    cin>>t>>s;

    DoubleStringHash ha;
    ha.init(s);
    int n = s.size();
    s = "?" + s;
    int res = 0;

    if(t[0] == '1')
        for(auto &it : t)
        {
            if(it == '0')   it = '1';
            else it = '0';
        }
    for(auto it : t)
        if(it == '0')   z++;
        else o++;
    for(int len = 1; len <= n; len++)
    {
        int l = 1;
        pll p0 = {-1, -1}, p1 = {-1, -1};
        if(1ll * z * len > n)   break;
        int len0 = z * len;
        int len1 = n - len0;
        if(len1 % o != 0 || len1 <= 0)   continue;
        int le1 = len1 / o;
        for(auto it : t)
        {
            if(it == '0')
            {
                if(p0.first == -1)
                    p0 = ha.get(l, l + len - 1);
                else if(!ha.same(p0, ha.get(l, l + len - 1))) 
                    continue;
                l = l + len;
            }
            else
            {
                if(p1.first == -1)
                    p1 = ha.get(l, l + le1 - 1);
                else if(!ha.same(p1, ha.get(l, l + le1 - 1)))
                    continue;
                l = l + le1;
            }
            //cout<<len<<" "<<le1<<"  "<<l<<"\n";
        }
        if(!ha.same(p0, p1) && l == n + 1)
        {
            //cout<<len<<"  p0 & p1"<<p0.first<<" "<<p0.second<<"   "<<p1.first<<"  "<<p1.second<<'\n';
            res++;
        }
    }
    cout<<res<<'\n';
    return;
}

RCC 2014 Warmup (Div. 2) D. Cunning Gena

状压dp,直接定义 \(f[n][1 << m]\) 这么大的数组会 MLE , 那么我们就用滚动数组就好了

先预处理出每个人的可以解决问题的二进制状态 \(st\),再进行已显示器数量 \(k\) 为关键字进行排序

转移方程 \(f_{i \& 1, S}\) 表示已经选到第 \(i\) 个人, 解决了问题的二进制状态为 \(S\) 的最小花费

\[f_{(i + 1) \& 1, S | st} = \begin{cases} \min f_{i \& 1, S} + x_i, & S | st \ne (1 << m) -1 \\ \min f_{i \& 1, S} + x_i + y_i \times b, & S | st = (1 << m) -1 \\ \end{cases} \]

const int N = 1e2 + 10;
const int M = 20;

ll f[2][1 << M];
ll n, m, b;
array<ll, 3> a[N];

void solve()
{       
    cin>>n>>m>>b;
    for(int i = 1; i <= n; i++)
    {
        ll x, y, z, s = 0; cin>>x>>y>>z;
        for(int j = 0; j < z; j++)
        {
            int t;  cin>>t; t--;
            s = s + (1 << t);
        }
        a[i] = {y, x, s};
    }
    sort(a + 1, a + 1 + n);

    for(int i = 0; i < 1 << M; i++)
        for(int j = 0; j <= 1; j++)
            f[j][i] = 3e18;
    f[1][0] = 0;
    // for(int i = 1; i <= n; i++)
    //     cout<<a[i][0]<<" "<<a[i][1]<<" "<<a[i][2]<<'\n';
    for(int i = 1; i <= n; i++)
    {
        for(int S = 0; S < (1 << m); S++)
        {
            f[(i + 1) & 1][S] = min(f[i & 1][S], f[(i + 1) & 1][S]);
            int st = S | a[i][2];
            if(st == (1 << m) - 1)
                f[(i + 1) & 1][st] = min(f[i & 1][S] + a[i][1] + a[i][0] * b, f[(i + 1) & 1][st]);
            else
                f[(i + 1) & 1][st] = min(f[i & 1][S] + a[i][1], f[(i + 1) & 1][st]);
            //cout<<i<<" "<<S<<"  "<<st<<" "<<f[(i + 1) & 1][st]<<'\n';
        }
    }
    if(f[(n + 1) & 1][(1 << m) - 1] >= 3e18) f[(n + 1) & 1][(1 << m) - 1] = -1;
    cout<<f[(n + 1) & 1][(1 << m) - 1]<<'\n';
    return;
}

Codeforces Round 867 (Div. 3) F. Gardening Friends

很板子的换根dp,第一遍dfs预处理出最长链,次长链,和深度,第二遍dfs计算来自父亲的最长链,这里特判如果对最长链的儿子转移,那么去父亲和次长链的最大值 + 1进行转移

const int N = 2e5 + 10;

int dep[N];
ll n, k, c;
ll f[N][2][2], g[N];
// 0最大,1次大
vector<int> e[N];
void dfs(int u, int from)
{
    dep[u] = dep[from] + 1;
    f[u][0][0] = 0, f[u][0][1] = u;
    for(auto v : e[u])
    {
        if(v == from)   continue;
        dfs(v, u);
        if(f[v][0][0] + 1 > f[u][0][0])
        {
            f[u][1][0] = f[u][0][0];
            f[u][1][1] = f[u][0][1];
            f[u][0][0] = f[v][0][0] + 1;
            f[u][0][1] = v;
        }
        else if(f[v][0][0] + 1 > f[u][1][0])
        {
            f[u][1][0] = f[v][0][0] + 1;
            f[u][1][1] = v;
        }
    }
}

void dfs2(int u, int from)
{
    for(auto v : e[u])
    {
        if(v == from)   continue;
        if(f[u][0][1] == v)
            g[v] = max(g[u] + 1, f[u][1][0] + 1);
        else 
            g[v] = max(g[u] + 1, f[u][0][0] + 1);
        dfs2(v, u);
    }
}

void solve()
{       
    cin>>n>>k>>c;
    for(int i = 1; i <= n; i++)
    {
        dep[i] = f[i][0][0] = f[i][0][1] = f[i][1][0] = f[i][1][1] = g[i] = 0;
        e[i].clear();
    }
    for(int i = 1; i < n; i++)
    {
        int u, v;   cin>>u>>v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    dfs(1, 0);
    dfs2(1, 0);
    ll res = 0;
    for(int i = 1; i <= n; i++)
        res = max(-(dep[i] - dep[1]) * c + max(f[i][0][0], g[i]) * k, res);
    // for(int i = 1; i <= n; i++)
    // {
    //     cout<<f[i][0][0]<<"  "<<f[i][0][1]<<"  "<<f[i][1][0]<<"   "<<f[i][1][1]<<"  "<<g[i]<<'\n';

    // }
    // cout<<"-----\n";
    cout<<res<<'\n';
    return;
}

Codeforces Round 296 (Div. 2) D. Clique Problem

离散化,线段树维护区间最值 ,dp

考虑从左往右dp,一个点 \(i\) 的影响范围是 \(x_i + w_i\), 那么这个点对范围 \([-\infty,x_i + w_i]\)的贡献是 \([-\infty, x_i - w_i]\) 的区间最大值 + 1,我们将所有的点存进来,离散化,按\(x_i + w_i\) 为第一关键字, \(x_i - w_i\) 为第二关键字进行排序,然后不断进行以上过程即可

\[f_{x_i + w _i} = \max_{j = -\infty}^{x_i + w_i} f_j + 1 \]

时间复杂度\(O(N \log N)\)

但其实可以发现可以按照线段覆盖的模型来写,就不用什么线段树了P1803 凌乱的yyy / 线段覆盖

const int N = 2e6 + 10;

int n;
array<ll, 2> a[N];
vector<ll> vx;
struct segtree
{
    int val;
}seg[N * 4];

void update(int id)
{
    seg[id].val = max(seg[id * 2].val, seg[id * 2 + 1].val);
}

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

int query(int id, int l, int r, int ql, int qr)
{
    if(ql == l && qr == r)
    {
        return seg[id].val;
    }
    int mid = (l + r) >> 1;
    if(qr <= mid)
        return query(id * 2, l, mid, ql, qr);
    else if(ql > mid)
        return query(id * 2 + 1, mid + 1, r, ql, qr);
    else 
        return max(query(id * 2, l, mid, ql, mid), query(id * 2 + 1, mid + 1, r, mid + 1, qr));
}

int get_id(ll t)
{
    return lower_bound(vx.begin(), vx.end(), t) - vx.begin() + 1;
}

void solve()
{       
    cin>>n;
    for(int i = 1; i <= n; i++)
    {
        ll x, w;    cin>>x>>w;
        a[i] = {x + w, x - w};
        vx.push_back(x - w);
        vx.push_back(x);
        vx.push_back(x + w);
    }
    sort(vx.begin(), vx.end());
    vx.erase(unique(vx.begin(), vx.end()), vx.end());
    int m = vx.size(), res = 0;
    sort(a + 1, a + 1 + n);
    for(int i = 1; i <= n; i++)
    {
        int id = get_id(a[i][1]);
        int add = query(1, 1, m, 1, id);
        id = get_id(a[i][0]);
        change(1, 1, m, id, add + 1);
        res = max(add + 1, res);
    }
    cout<<res<<'\n';
    return;
}

Codeforces Beta Round 52 (Div. 2) E. Domino Principle

线段树,离散化

观察得,倒向右边会压到骨牌,这个骨牌倒下又会压到其他的骨牌

所以我们肯定要倒着做,按坐标排序,用线段树维护倒下后最大的距离,维护区间中的骨牌数量

没看到坐标可以是负数,wa了几发

const int N = 3e5 + 10;

int n, pos[N], res[N];
array<int, 3> a[N];
vector<int> vx;
struct segtree
{
    int mav, val;
}seg[N * 4];


void update(int id)
{
    seg[id].mav = max(seg[id * 2].mav, seg[id * 2 + 1].mav);
    seg[id].val = seg[id * 2].val + seg[id * 2 + 1].val;
}

void change(int id, int l, int r, int pos, int tag)
{
    if(l == r)
    {
        seg[id].mav = max(seg[id].mav, tag);
        return;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid)
        change(id * 2, l, mid, pos, tag);
    else 
        change(id * 2 + 1, mid + 1, r, pos ,tag);
    update(id);
}
void change2(int id, int l, int r, int pos, int tag)
{
    if(l == r)
    {
        seg[id].val = seg[id].val + tag;
        return;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid)
        change2(id * 2, l, mid, pos, tag);
    else 
        change2(id * 2 + 1, mid + 1, r, pos ,tag);
    update(id);
}

int query(int id, int l, int r, int ql, int qr)
{
    if(ql == l && qr == r)
    {
        return seg[id].mav;
    }
    int mid = (l + r) >> 1;
    if(qr <= mid)
        return query(id * 2, l, mid, ql, qr);
    else if(ql > mid)
        return query(id * 2 + 1, mid + 1, r, ql, qr);
    else 
        return max(query(id * 2, l, mid, ql, mid), query(id * 2 + 1, mid + 1, r, mid + 1, qr));
}

int query2(int id, int l, int r, int ql, int qr)
{
    if(ql == l && qr == r)
    {
        return seg[id].val;
    }
    int mid = (l + r) >> 1;
    if(qr <= mid)
        return query2(id * 2, l, mid, ql, qr);
    else if(ql > mid)
        return query2(id * 2 + 1, mid + 1, r, ql, qr);
    else 
        return query2(id * 2, l, mid, ql, mid) + query2(id * 2 + 1, mid + 1, r, mid + 1, qr);
}

void solve()
{       
    cin>>n;
    vx.push_back(0);
    for(int i = 1; i <= 12 * n; i++)
        seg[i].mav = -1e9;
    for(int i = 1; i <= n; i++)
    {
        cin>>a[i][0]>>a[i][1];
        a[i][1] = a[i][0] + a[i][1] - 1;
        a[i][2] = i;
        vx.push_back(a[i][0]);
        vx.push_back(a[i][0] + 1);
        vx.push_back(a[i][1]);
    }
    sort(vx.begin(), vx.end());
    vx.erase(unique(vx.begin(), vx.end()), vx.end());
    int m = vx.size();
    sort(a + 1, a + 1 + n);
    for(int i = n; i >= 1; i--)
    {
        int l = a[i][0] + 1, r = a[i][1];
        int pl = lower_bound(vx.begin(), vx.end(), l) - vx.begin() + 1;
        int pr = lower_bound(vx.begin(), vx.end(), r) - vx.begin() + 1;
        r= max(query(1, 1, m, pl, pr), r);

        pr = lower_bound(vx.begin(), vx.end(), r) - vx.begin() + 1;
        pl = lower_bound(vx.begin(), vx.end(), l - 1) - vx.begin() + 1;
        change(1, 1, m, pl, r);

        pl = lower_bound(vx.begin(), vx.end(), l - 1) - vx.begin() + 1;
        change2(1, 1, m, pl, 1);
        pl = lower_bound(vx.begin(), vx.end(), l) - vx.begin() + 1;
        res[a[i][2]] = query2(1, 1, m, pl, pr) + 1;
    }
    for(int i = 1; i <= n; i++)
        cout<<res[i]<<' ';
    cout<<'\n';
    return;
}

Codeforces Round 212 (Div. 2) C. Insertion Sort

一眼 \(O(N ^ 2)\) ,鉴定为真

不错的好题

冒泡排序交换相邻元素的次数是逆序对之和,也就是说交换两个元素\(i,j(i \ne j,1 \leq i \leq n)\) ,要使得逆序对之和最小,交换两个元素会对逆序对之和产生什么变化呢?

设逆序对 \(f(x) = \sum_{i = 1}^{x - 1} [a_i > a_x]\), 逆序对之和为 \(sum = \sum_{i = 1}^{n}f(i)\),有这么一个序列 \(\dots,a_x,\dots,a_y,\dots\) 考虑交换 \(a\) ,\(b\)

交换后的逆序对为

\[sum = sum + f(x) + \sum_{i = x + 1}^{y - 1} [a_i > a_x] - \sum_{i = x + 1}^{y - 1} [a_i < a_x] + f(y) - \sum_{i = x + 1}^{y - 1} [a_i > a_y] - (a > b) + (a < b) \]

那么步骤:

  1. 我们先算出各个点的逆序对\(f(x)\)

  2. 预处理出交换后逆序对变化(注意x向前交换和向后交换计算方式不一样)

  3. 暴力枚举 \(x\) ,\(y\)

const int N = 5e3 + 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, a[N], b[N][N], ni[N];
void solve()
{       
    cin>>n;
    for(int i = 1; i <= n; i++)
    {
        cin>>a[i];
        a[i]++;
    }
    tr.resize(n);
    int s = 0;
    for(int i = 1; i <= n; i++)
    {
        ni[i] = tr.query(n) - tr.query(a[i]);
        s += ni[i];
        tr.modify(a[i], 1);
    }
    for(int i = 1;  i <= n; i++)
    {
        b[i][i] = ni[i];
        for(int j = i - 1; j >= 1; j--)
        {
            b[i][j] = b[i][j + 1];
            if(a[j] > a[i]) b[i][j]--;
            else if(a[i] > a[j]) b[i][j]++;
        }
        for(int j = i + 1; j <= n; j++)
        {
            b[i][j] = b[i][j - 1];
            if(a[j] < a[i]) b[i][j]--;
            else if(a[j] > a[i])    b[i][j]++;
        }
    }
    int miv = s, res = 0;
    //cout<<s<<'\n';
    for(int i = 1;  i <= n; i++)
        for(int j = i + 1; j <= n; j++)
        {
            int t = b[i][j - 1] + b[j][i + 1];
            //cout<<i<<" "<<j<<"  "<<t<<"   "<<s - ni[i] - ni[j] + t + -(a[i] > a[j]) + (a[j] > a[i])<<'\n';
            if(s - ni[i] - ni[j] + t + -(a[i] > a[j]) + (a[j] > a[i]) < miv)
            {
                res= 0;
                miv = s - ni[i] - ni[j] + t + -(a[i] > a[j]) + (a[j] > a[i]);
            }
            if(s - ni[i] - ni[j] + t + -(a[i] > a[j]) == miv)
                res++;
        }
    cout<<miv<<" "<<res.size()<<'\n';
    return;
}

VK Cup 2015 - Round 2 (unofficial online mirror, Div. 1 only) B. Work Group

树形dp

满足节点 \(u\) 其子树大小为奇数,偶数均可,区别在于有没有考虑 \(u\) 结点本身

对于转移方程

\[f_{u,0} = \max (f_{u,0} + f_{v,0},f_{u,1}+f_{v,1})\\ f_{u,1} = \max (f_{u,0} + f_{v,1},f_{u,1}+f_{v,0}) \]

const int N = 2e5 + 10;

int n;
ll w[N], f[N][2];
// 0 有自己 1 没有自己
vector<int> e[N];
void dfs(int u, int from)
{
    f[u][1] = -(1ll << 60);
    for(auto v : e[u])
    {
        if(v == from)   continue;        
        dfs(v, u);
        ll a = f[u][0], b = f[u][1];
        f[u][0] = max(a + f[v][0], b + f[v][1]);
        f[u][1] = max(a + f[v][1], b + f[v][0]);        
    }
    f[u][1] = max(f[u][0] + w[u], f[u][1]);
}

void solve()
{       
    cin>>n;
    for(int i = 1; i <= n; i++)
    {
        int p;  cin>>p>>w[i];
        if(p != -1)  e[p].push_back(i);
    }
    dfs(1, 0);
    // for(int i = 1; i <= n; i++)
    //     cout<<f[i]<<'\n';
    cout<<max(f[1][0], f[1][1])<<'\n';
    return;
}

Codeforces Round 874 (Div. 3) F. Ira and Flamenco

思维题

问长度为 \(k\) ,公差为 \(1\),的等差数列有多少个,离散化,双指针判断即可

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, k;
map<int, int> mp;
/*
8 2
1 1 2 2 3 3 3 5
1 2 3 5
*/
void solve()
{       
    vector<int> a;
    mp.clear();
    cin>>n>>k;
    for(int i = 1; i <= n; i++)
    {
        int x;  cin>>x;
        mp[x]++;    a.push_back(x);
    }
    sort(a.begin(), a.end());
    a.erase(unique(a.begin(), a.end()), a.end());
    int res = 0, f = 1;
    int m = a.size();
    for(int i = 0; i < m; i++)
    {
        if(i + k - 1 < m && a[i] + k - 1 == a[i + k - 1])
        {
            f = 1;
            for(int j = i; j <= i + k - 1; j++)
                f = 1ll * f * mp[a[j]] % mod;
            res = (1ll * res + f) % mod;
            int l = i, r = i + k - 1;
            while(r + 1 < m && a[r + 1] == a[r] + 1)
            {
                f = 1ll * f * qmi(mp[a[l]], mod - 2, mod) % mod;
                f = 1ll * f * mp[a[r + 1]] % mod;
                res = (1ll * res + f) % mod;
                l++, r++;
            }
            i = r;
        }
    }
    cout<<res<<'\n';
    return;
}

Codeforces Round 455 (Div. 2) D - Colorful Points

链表

同色分为一组,通过双链表去进行删除和合并操作,复杂度 \(O(N)\)

const int N = 1e6 + 10;

int m, l[N], r[N], e[N], num[N], times[N], idx;
void insert(int a, int x, int w, int t)
{
    times[idx] = t;
    num[idx] = w;
    e[idx] = x;

    l[idx] = a;
    l[r[a]]= idx;
    r[idx] = r[a];
    r[a] = idx;
    idx++;
}
void del(int a)
{
    r[l[a]] = r[a];
    l[r[a]] = l[a];
}
void solve()
{       
    string t;   cin>>t;
    int n = t.size(), k = 1;
    r[0] = 1, l[1] = 0, idx = 2;
    for(int i = 0; i < n; i++)
    {
        int x = i, y = i; 
        while(y + 1 < n && t[y + 1] == t[x])
            y++;
        insert(l[1], y - x + 1, t[i] - 'a' + 1, idx);
        k++;
        i = y;
    }
    int res = 0, tt = 0;
    while(1)
    {
        bool ok = false;
        vector<int> d;
        for(int i = r[0]; i != 1; i = r[i])
        {
            int tl = l[i], tr = r[i];
            if(tl == tr)    break;
            if(tl != 0 && num[tl] != num[i])
            {
                ok = true;
                e[i]--;
            }
            if(tr != 1 && num[tr] != num[i])
            {
                ok = true;
                e[i]--;
            }
            if(tr != 1 && num[tr] == num[i])
            {
                e[tr] = e[tr] + max(e[i], 0);
                e[i] = 0;
            }
            if(e[i] <= 0)
                d.push_back(times[i]);
        }
        if(!ok) break;
        for(auto it : d)
            del(it);
        res++;
    }
    cout<<res<<'\n';
    return;
}

Codeforces Round 870 (Div. 2) D. Running Miles

问 \(b_i + b_j + b_k - (r - l), i \ne j, i \ne k, j \ne k\) 的最大值

变化一下\(b_l + l + b_j + b_r - r\)

为什么呢对于一段区间 \([l, r]\), 显然使\(l\) 越大, \(r\) 越小是最优的

预处理出后缀 \(b_r - r\) 的最大值,再预处理每个点加上后缀最大值的值 \(b_j + \max_{r = j + 1}^{n} (b_r - r)\) ,再预处理 \(b_j + \max_{r = j + 1}^{n} (b_r - r)\) 的后缀最大值

总的来说就是后缀最大值的嵌套

const int N = 2e5 + 10;

int n, a[N], pre[N], mid[N], suf[N];

void solve()
{       
    cin>>n;
    for(int i = 1; i <= n; i++) 
    {
        cin>>a[i];
        pre[i] = a[i] + i;
    }
    suf[n] = a[n] - n;
    for(int i = n - 1; i >= 1; i--)
        suf[i] = max(a[i] - i, suf[i + 1]);
    mid[n] = -(1 << 29);
    for(int i = n - 1; i >= 2; i--)
        mid[i] = max(suf[i + 1] + a[i], mid[i + 1]);
    int res = 0;
    for(int i = 1; i <= n - 2; i++)
        res = max(pre[i] + mid[i + 1], res);
    cout<<res<<"\n";
    return;
}

Codeforces Round 179 (Div. 2) B. Yaroslav and Two Strings

一眼dp

$ f_{i,0,1,2,3}$ 表示情况都不满足,情况\(s < w\) 满足,, 情况 \(s > w\) 满足,情况 \(s < w, s > w\) 满足,相互之间转移即可

int n;
int f[N][4];
string s, t;    
// 0    1   2   3
// 0    <   >   <>
void solve()
{       
    cin>>n;
    cin>>s>>t;
    s = "?" + s, t = "?" + t;
    int S = 0;
    for(int i = 1; i <= n; i++)
        if(s[i] == '?' || t[i] == '?')
            continue;
        else if(s[i] < t[i])
            S = S | (1 << 1);
        else if(s[i] > t[i])
            S = S | (1 << 2);


    if(S == 0)  f[0][0] = 1;
    else if(S == 2) f[0][1] = 1;
    else if(S == 4) f[0][2] = 1;
    else if(S == 6) f[0][3] = 1;

    for(int i = 1; i <= n; i++)
    {
        if(t[i] != '?' && s[i] != '?')
        {
            f[i][0] = f[i - 1][0];
            f[i][1] = f[i - 1][1];
            f[i][2] = f[i - 1][2];
            f[i][3] = f[i - 1][3];
            continue;
        }
        else if(s[i] == '?' && t[i] == '?')
        {
            f[i][0] = 1ll * f[i - 1][0] * 10 % mod;
            f[i][1] = (1ll * f[i - 1][1] * 55 + 1ll * f[i - 1][0] * 45) % mod;
            f[i][2] = (1ll * f[i - 1][2] * 55 + 1ll * f[i - 1][0] * 45) % mod;
            f[i][3] = (1ll * f[i - 1][3] * 100 + 1ll * f[i - 1][1] * 45 + 1ll * f[i - 1][2] * 45) % mod;
        }
        else if(s[i] == '?' || t[i] == '?')
        {
            // 0    1   2   3
            // 0    <   >   <>            
            if(s[i] == '?')
            {
                f[i][0] = f[i - 1][0];
                f[i][1] = (1ll * f[i - 1][0] * (t[i] - '0') + 1ll * f[i - 1][1] * (t[i] - '0' + 1)) % mod;
                f[i][2] = (1ll * f[i - 1][0] * ('9' - t[i]) + 1ll * f[i - 1][2] * ('9' - t[i] + 1)) % mod;
                f[i][3] = (1ll * f[i - 1][3] * 10 + 1ll * f[i - 1][1] * ('9' - t[i]) + 1ll * f[i - 1][2] * (t[i] - '0')) % mod;
            }
            else
            {
                f[i][0] = f[i - 1][0];
                f[i][1] = (1ll * f[i - 1][0] * ('9' - s[i]) + 1ll * f[i - 1][1] * ('9' - s[i] + 1)) % mod;
                f[i][2] = (1ll * f[i - 1][0] * (s[i] - '0') + 1ll * f[i - 1][2] * (s[i] - '0' + 1)) % mod;
                f[i][3] = (1ll * f[i - 1][3] * 10 + 1ll * f[i - 1][1] * (s[i] - '0') + 1ll * f[i - 1][2] * ('9' - s[i])) % mod;
            }
        }
        //cout<<i<<" "<<f[i][0]<<" "<<f[i][1]<<" "<<f[i][2]<<" "<<f[i][3]<<"\n-------------------\n";
    }
    cout<<f[n][3]<<'\n';
    return;
}

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

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