CSP-S 32 && 多校5

10.15 S-32&&多校5

签到后遗憾离场。

t1

签到题,大力分讨即可。

注意符号。

记得排序(没大阳历时忘排序了)

code

锵锵
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int T, m, n;
struct node
{
    int tim, val;
} a[N];

inline bool cmp(node a, node b)
{
    return a.tim < b.tim;
}

signed main()
{
    freopen("count.in", "r", stdin);
    freopen("count.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> T;
    while (T--)
    {
        cin >> m >> n;
        bool flag = 1;
        int lastim = 0, lasval = 0;
        for (int i = 1; i <= n; ++i)
            cin >> a[i].tim >> a[i].val;
        sort(a + 1, a + 1 + n, cmp);
        for (int i = 1; i <= n; ++i)
        {
            int tim = a[i].tim, val = a[i].val;
            if (!flag)
                continue;
            if (!val)
            {
                lastim = tim, lasval = val;
                continue;
            }
            if (!lasval)
            {
                if (abs(val) > tim - lastim)
                    flag = 0;
                lastim = tim, lasval = val;
                continue;
            }
            if ((val > 0 && lasval > 0) || (val < 0 && lasval < 0))
            {
                if (tim - lastim >= abs(val) + 1) // 0
                {
                    lastim = tim, lasval = val;
                    continue;
                }
                if (abs(val - lasval) > tim - lastim)
                {
                    flag = 0;
                    continue;
                }
                if ((tim - lastim - abs(val - lasval)) % 2 == 0)
                    lastim = tim, lasval = val;
                else
                    flag = 0;
            }
            else
            {
                if (tim - lastim >= abs(val) + 1) // 0
                    lastim = tim, lasval = val;
                else
                    flag = 0;
            }
        }
        if (flag)
            cout << "Yes\n";
        else
            cout << "No\n";
    }
    return 0;
}

t2

若读假题面(谁这么唐我不说),将积看成和,则对于每个贡献值计算出其出现次数即可(易得这个次数为 2 的幂)。

可惜题面为积 \(\ldots\)

先将答案表达式拆开,将积的平方拆为平方的积,之后便满足分配律。

考虑 \(dp_i\)\(1\)\(i\) 的答案,则终态为 \(dp_n\) .

\(s\) 数组为异或前缀和,易得转移方程式:

\(dp_i=\sum_{j=0}^{i-1}dp_j*(s_i\oplus s_j)^2\)

这步其实是好推的,但赛时被读假题面后的思路局限住了(太唐了)。

考虑如何做到正解:

发现平方和这个东西和异或在一起根本没法计算,于是将异或拆位,并先忽略平方

\(r_i\)\(r\) 二进制下第 \(i\) 位。

具体的,两数 \(x,y\) 异或为:

\(ans=\sum_{i=0}^{len}2^k[x_i=y_i]\)

这里 len 为二进制表示下的长度。

于是 dp 式子可化为:

\(dp_i=\sum_{j=0}^{i-1}dp_j\sum_{k=0}^{len}2^k[s(i)_k=s(j)_k]\)

即:

\(dp_i=\sum_{k=0}^{len}2^k\sum_{j=0}^{i-1}dp_j[s(i)_k=s(j)_k]\)

发现对于后一个 \(\sum\) 中的东西是一个类似前缀和的形式且 \([s(i)_k=s(j)_k]\)\(s_i\) 不变,启发我们进行前缀和优化。

具体的,记 \(sum_{i,0/1}\) 表示二进制下第 \(i\) 位为 \(0/1\)\(dp\) 值的和。

\(dp_i=\sum_{k=0}^{len}2^ksum_{k,s(i)_k\oplus 1}\)

\(sum_{k,i}=\sum_{k=0}^{len}dp_i[s(i)_k\oplus 1=i]\)

此时时间复杂度为 \(O(n\log_n)\),可过。

but \(\ldots\)

我们刚才忽略了平方,那么加上平方后怎么做呢?

加上平方后,唯一难处理的项还是异或。

此时要对 \(\sum_{k=0}^{len}2^k[s(i)_k=s(j)_k]\) 进行平方。

考虑 \(\sum_{i=1}^nc_i\) 进行平方,展开为 \(\sum_{i=1}^n\sum_{j=1}^nc_ic_j\),类似的,\(\sum_{k=0}^{len}2^k[s(i)_k=s(j)_k]\) 即为 \(\sum_{k1=0}^{len}\sum_{k2=0}^{len}2^{k1}2^{k2}[s(i)_{k1}=s(j)_{k1} \land s(i)_{k2}=s(j)_{k2}]\)

于是转移方程为:

\(dp_i=\sum_{k1=0}^{len}\sum_{k2=0}^{len}2^{k1}2^{k2}[s(i)_{k1}=s(j)_{k1} \land s(i)_{k2}=s(j)_{k2}]\sum_{j=0}^{i-1}dp_j\)

同上,我们记 \(sum_{i,j,0/1,0/1}\),表示二进制下第 \(i\) 位为 \(0/1\) ,第 \(j\) 位为 \(0/1\)\(dp\) 值的和。

之后同理转移即可。

记得赋初值!!!

code

噔噔
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
int T, n;
int a[N], dp[N], s[N];
int sum[33][33][2][2], lw[70];

signed main()
{
    freopen("partition.in", "r", stdin);
    freopen("partition.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    lw[0] = 1;
    for (int i = 1; i < 70; ++i)
        lw[i] = lw[i - 1] * 2 % mod;
    cin >> T;
    while (T--)
    {
        memset(dp, 0, sizeof(dp));
        memset(sum, 0, sizeof(sum));
        cin >> n;
        for (int i = 1; i <= n; ++i)
            cin >> a[i];
        for (int i = 1; i <= n; ++i)
            s[i] = s[i - 1] ^ a[i];

        dp[0] = 1;
        for (int k1 = 0; k1 <= 32; ++k1)
            for (int k2 = 0; k2 <= 32; ++k2)
                sum[k1][k2][0][0] += 1;

        for (int i = 1; i <= n; ++i)
        {
            int num = 0;
            for (int k1 = 0; k1 <= 32; ++k1)
                for (int k2 = 0; k2 <= 32; ++k2)
                {
                    num += lw[k1 + k2] * sum[k1][k2][!((s[i] >> k1) & 1)][!((s[i] >> k2) & 1)] % mod;
                    num %= mod;
                }
            // cerr << "num=" << num << "\n";
            dp[i] = num;
            for (int k1 = 0; k1 <= 32; ++k1)
                for (int k2 = 0; k2 <= 32; ++k2)
                {
                    sum[k1][k2][(s[i] >> k1) & 1][(s[i] >> k2) & 1] += dp[i];
                    sum[k1][k2][(s[i] >> k1) & 1][(s[i] >> k2) & 1] %= mod;
                }
        }
        cout << dp[n] << "\n";
    }
    return 0;
}

t3

要求极差最小,我们可以考虑枚举最小值,找到其对应的最大值的最小值,作差后取最小即可。

如果太多最大值最小值容易弄混,可以将其均转化成含 \(\min\)\(\max\) 的公式。

考虑最小值种类不同的个数是 \(O(n)\) 级别的(至多有 \(2n-1\) 个)

因此对于每次查找最大值要求在 \(O(log_n)\) 的复杂度左右求出。

区间问题,\(log\) 级别复杂度 \(\ldots\)

就是线段树!

但是如何维护信息呢?

题目中有一个重要限制:每组包含恰好一个任务,或恰好两个相邻的任务

对于一个任务,自然就是叶子结点本身维护的一个值,而对于恰好两个相邻的任务,考虑在每个叶子结点上维护 \(dp_{0/1,0/1}\)

\(dp_{0,0}\) 表示该结点所表示区间中分组均在区间内;
\(dp_{1,0}\) 表示该结点所表示区间中分组左侧超出区间;
\(dp_{0,1}\) 表示该结点所表示区间中分组右侧超出区间;
\(dp_{1,1}\) 表示该结点所表示区间中分组左,右均侧超出区间;

对于 pushup 函数按照定义转移即可。

pushup
inline void pushup(int id)
{
    dp[id][0][0] = min(max(dp[lid][0][0], dp[rid][0][0]), max(dp[lid][0][1], dp[rid][1][0]));
    dp[id][0][1] = min(max(dp[lid][0][0], dp[rid][0][1]), max(dp[lid][0][1], dp[rid][1][1]));
    dp[id][1][0] = min(max(dp[lid][1][0], dp[rid][0][0]), max(dp[lid][1][1], dp[rid][1][0]));
    dp[id][1][1] = min(max(dp[lid][1][0], dp[rid][0][1]), max(dp[lid][1][1], dp[rid][1][1]));
}

如此操作,\(dp_{1,0,0}\) 即为最大值的最小值 。

我们将所有可能的最小值去重后按升序排序,依次取出,统计答案后在线段树上去除参与构成当前最小值的点产生的贡献,即将对应点赋值为 inf 。

注意操作细节,特别注意边界。

code

哒哒哒

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 10;
const int inf = 1e18;
int T, n, cnt;
int a[N], b[N], c[N];
unordered_map<int, vector< pair<int, int> > > mp;
int dp[N << 2][2][2];
struct node
{
    int l, r;
} t[N << 2];
#define lid (id << 1)
#define rid (id << 1 | 1)

inline void pushup(int id)
{
    dp[id][0][0] = min(max(dp[lid][0][0], dp[rid][0][0]), max(dp[lid][0][1], dp[rid][1][0]));
    dp[id][0][1] = min(max(dp[lid][0][0], dp[rid][0][1]), max(dp[lid][0][1], dp[rid][1][1]));
    dp[id][1][0] = min(max(dp[lid][1][0], dp[rid][0][0]), max(dp[lid][1][1], dp[rid][1][0]));
    dp[id][1][1] = min(max(dp[lid][1][0], dp[rid][0][1]), max(dp[lid][1][1], dp[rid][1][1]));
}

inline void build(int id, int l, int r)
{
    t[id].l = l, t[id].r = r;
    if (l == r)
    {
        dp[id][0][0] = a[l];
        dp[id][1][0] = a[l] + a[l - 1];
        dp[id][0][1] = a[l] + a[l + 1];
        dp[id][1][1] = inf;
        return;
    }
    int mid = (l + r) >> 1;
    build(lid, l, mid);
    build(rid, mid + 1, r);
    pushup(id);
}

inline void update(int id, int pos, int opt)
{
    if (t[id].l == t[id].r && t[id].l == pos)
    {
        // if (!opt)
        if (a[pos] <= opt)
            dp[id][0][0] = inf;
        // else if (opt == 1)
        if (pos == 1 || (pos > 1 && a[pos - 1] + a[pos] <= opt))
            dp[id][1][0] = inf;
        // else
        if (pos == n || (pos < n && a[pos + 1] + a[pos] <= opt))
            dp[id][0][1] = inf;
        return;
    }
    int mid = (t[id].l + t[id].r) >> 1;
    if (mid >= pos)
        update(lid, pos, opt);
    else
        update(rid, pos, opt);
    pushup(id);
}

void Deb(int l, int r, int p)
{
    cout << "[" << l << "," << r << "]" << " p=" << p << "\n";
    cout << "dp[p][0][0]=" << dp[p][0][0] << "\n";
    cout << "dp[p][0][1]=" << dp[p][0][1] << "\n";
    cout << "dp[p][1][0]=" << dp[p][1][0] << "\n";
    cout << "dp[p][1][1]=" << dp[p][1][1] << "\n\n";

    int mid = (l + r) >> 1;
    if (l == r)
        return;
    Deb(l, mid, p << 1);
    Deb(mid + 1, r, p << 1 | 1);
}

signed main()
{
    freopen("opti.in", "r", stdin);
    freopen("opti.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> T;
    while (T--)
    {
        mp.clear();
        cnt = 0;
        cin >> n;
        for (int i = 1; i <= n; ++i)
            cin >> a[i];
        for (int i = 1; i < n; ++i)
            b[i] = a[i] + a[i + 1];
        for (int i = 1; i < n; ++i)
        {
            c[++cnt] = a[i], c[++cnt] = b[i];
            mp[a[i]].push_back({i, 0});
            mp[b[i]].push_back({i, i + 1});
        }
        mp[a[n]].push_back({n, 0});
        c[++cnt] = a[n];
        a[0] = a[n + 1] = inf;
        sort(c + 1, c + 1 + cnt);
        cnt = unique(c + 1, c + 1 + cnt) - c - 1;
        build(1, 1, n);
        int ans = inf;

        // for(int i=1;i<=cnt;i++) cout<<c[i]<<" ";
        // cout<<"\n";
        for (int i = 1; i <= cnt; ++i)
        {
            if(dp[1][0][0]>=inf) break;
            ans = min(ans, dp[1][0][0] - c[i]);
            // cout << "mx=" << dp[1][0][0] <<" "<<c[i]<< "\n";
            // Deb(1,n,1);
            // cerr<<"siz="<<mp[c[i]].size()<<"\n";
            for (auto y : mp[c[i]])
            {
                if (!y.second)
                    update(1, y.first, c[i]);
                else
                    update(1, y.first, c[i]), update(1, y.second, c[i]);
            }
            // cout<<"\n------------------------------\n\n\n";
        }
        cout << ans << "\n";
    }
    return 0;
}

t4

咕咕嘎嘎

posted @ 2025-10-20 21:50  HS_fu3  阅读(24)  评论(0)    收藏  举报