CCPC Henan Provincial Contest 2020

CCPC Henan Provincial Contest 2020

Problem B. 广告投放

n集节目按顺序播出,节目组决定在某些节目中投放广告,节目最初播出时有m名观众,若\(i\)集投放广告,若此时还剩\(c\)名观众,那么产生\(c*p_i\)的收益,但播出后会使得观众人数\(c'=\lfloor c/d_i \rfloor\),即\(i+1\)集只会剩下\(c'\)名观众观看,如果在第\(i\)集没有投放广告,则不会产生收益,观众人数也不会减少

\(n<=1e5\)

题解:数论分块 + 线性\(DP\): \(O(n\sqrt{n}\))

首先引入重要引理:

  1. \(\lfloor \lfloor n/i \rfloor /j \rfloor = \lfloor n/(i*j) \rfloor\)
  2. \(\lfloor n/i\rfloor\)的取值只有\(O(\sqrt{n})\)种取值

显然是线性\(dp\),根据题意我们得到:

状态表示:\(f[i][j]\):代表在第\(i\)集人数为\(j\)时能够获得的最大收益

状态属性:\(MAX\)

状态转移:

  1. \(i\)集投放广告,那么第\(i+1\)集的人数会减少,但会得到收益:\(f[i+1][j/d[i]] = max(f[i+1][j/d[i]],f[i][j]+c*p[i])\)

  2. \(i\)集不投放广告,那么第\(i+1\)集的人数和收益都不变:

\(f[i+1][j] = max(f[i+1][j],f[i][j])\)

状态优化:

  1. 显然二维数组开不下,会\(MLE\),我们利用滚动数组实现,我们发现第\(i\)层由第\(i-1\)层转移得到,且我们发现转移的方向为\(j->j/d[i]\)\(j->j\),即

那么这就说明我们可以从前往后遍历人数,所以优化后的转移方程为:\(f[j/d[i]] = max(f[j/d[i],f[j]+c*p[i]])\)

  1. 那么我们解决了空间问题,还需要解决\(TLE\),因为现在时间复杂度为\(O(n^2)\),我们发现第二维在转移的时候存在这样的情况\(j->\lfloor j/d[i]\rfloor -> \lfloor \lfloor j/d[i] \rfloor /d[i+1] \rfloor\),通过引理1得到\(j->\lfloor j/d[i]\rfloor -> \lfloor j/(d[i]*d[i+1]) \rfloor\),通过引理2得到第二维\(j\)的取值只有\(O(\sqrt{n})\)种取值,我们只需要预处理出第二维的所有取值,然后从前往后遍历即可

状态初始:\(f[i] = 0\)

答案呈现:遍历一遍第\(m\)集的每个人数时的最大收益,取\(max\)即可得到答案

#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10;

int n, m;
int p[N];
int d[N];
int f[N];
int a[N];
map<int, int> mp;

void solve()
{
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        cin >> p[i];
    for (int i = 1; i <= n; ++i)
        cin >> d[i];
    int idx = 0;
    a[++idx] = 0;					 //注意别漏掉0了
    for (int i = m; i >= 1; --i)	//为了使人数升序,方便从前往后遍历
    {
        if (!mp[m / i])
        {
            mp[m / i]++;
            a[++idx] = m / i;
        }
    }
    for (int i = 1; i <= n; ++i)
    {
        for (int j = 1; j <= idx; ++j)
        {
            f[a[j] / d[i]] = max(f[a[j]] + a[j] * p[i], f[a[j] / d[i]]);
        }
    }
    int ans = -INF;
    for (int i = 0; i <= m; ++i)
    {
        ans = max(f[i], ans);
    }
    cout << ans << endl;
}
signed main(void)
{
    Zeoy;
    int T = 1;
    // cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

Problem E. 发通知

学院一共有\(n\)名学生,每名学生只会在\([a_i,b_i]\)收到通知,如果第\(i\)名学生收到通知会产生\(w_i\)的愉悦度,辅导员会选择在某个时刻发布通知,他希望至少有\(k\)名同学收到通知,同学们的总体愉悦度是所有收到通知的同学愉悦度的异或和,求最大的总体愉悦度

\(1<=a_i<=b_i<=1e9,1<=n<=5e5\)

题解:差分 + 离散化 : 差分的离散化需要注意

对于区间求数量问题,我们很容易想到差分,但是对于异或和我们怎么处理呢?异或和到底能不能差分?

实际上我们发现异或和天然满足差分后求前缀和的性质

但是现在区间数值太大,我们考虑对区间进行离散化处理,注意这时候我们需要离散化的是\(a_i和b_i+1\),如果对\(b_i\)离散化,那么最后对离散化后的结果+1,一定会产生错误,所以我们一定要在一开始的时候将\(b_i+1\)进行离散化

最后前缀和,如果发现人数超过\(k\),对当前答案取\(max\)即可

#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 5e5 + 10, M = 4e5 + 10;

int n, k;
int a[N], b[N], w[N];
int dif1[N << 1], dif2[N << 1];
vector<int> v;

void solve()
{
    cin >> n >> k;
    for (int i = 1; i <= n; ++i)
    {
        cin >> a[i] >> b[i] >> w[i];
        v.push_back(a[i]);
        v.push_back(b[i] + 1);		//注意离散化的是b[i]+1
    }
    sort(all(v));
    v.erase(unique(all(v)), v.end());
    int cnt = v.size();
    for (int i = 1; i <= n; ++i)
    {
        int l = lower_bound(all(v), a[i]) - v.begin() + 1;
        int r = lower_bound(all(v), b[i] + 1) - v.begin() + 1;
        dif1[l]++;
        dif1[r]--;
        dif2[l] ^= w[i];
        dif2[r] ^= w[i];
    }
    int ans = -INF;
    for (int i = 1; i <= cnt; ++i)
    {
        dif1[i] = dif1[i - 1] + dif1[i];
        dif2[i] = dif2[i - 1] ^ dif2[i];
        if (dif1[i] >= k)
            ans = max(ans, dif2[i]);
    }
    if (ans == -INF)
        cout << -1 << endl;
    else
        cout << ans << endl;
}
signed main(void)
{
    Zeoy;
    int T = 1;
    // cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

Problem J. 二进制与、平方和

维护一个长度为\(n\)的非负整数序列\(a\),支持两种操作:

  1. 给定\([l,r]\)\(x\),将区间中的每个元素\(a_i\)修改为各自和\(x\)的与的值,即\(a_i:=a_i\&x\)
  2. 给定\([l,r]\),询问区间中所有元素的平方和

共有\(q\)次操作,\(a_i<2^{24}\)

题解:在线段树上递归\(O(n·logn+q·loga)\)

我们可以在线段树上维护”区间或“和区间平方和

  1. 如果一个区间中的区间或&\(x\)后值没有改变,说明该区间中没有必要去修改,也就是说区间中没有元素的值&\(x\)后会发生变化
  2. 如果一个区间中的区间或&\(x\)后值发生改变,说明一定有元素\(a_i\)需要修改,我们递归到子节点对子节点进行修改
  3. 我们考虑在线段树上递归的复杂度:首先\(a_i<2^{24}\),也就是说最坏的情况下每个位置最多会变化,并且我们维护的是区间或,叶子节点的变化一定会涉及到它的所有祖先节点,所以复杂度实际不高为\(O(n·logn+q·loga)\)

对于本题实际上还可以维护每个区间中每一位上0和1的个数,因为最多维护24位,但复杂度略高,勉强跑过

#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 998244353;
const double eps = 1e-9;
const int N = 3e5 + 10, M = 4e5 + 10;

int n, q;
int a[N];
struct node
{
    int sum;
    int orsum;
} seg[N << 2];

void up(int id)
{
    seg[id].sum = (seg[lson].sum + seg[rson].sum) % mod;
    seg[id].orsum = seg[lson].orsum | seg[rson].orsum;
}

void build(int id, int l, int r)
{
    if (l == r)
    {
        seg[id].sum = a[l] * a[l] % mod;
        seg[id].orsum = a[l];
        return;
    }
    int mid = l + r >> 1;
    build(lson, l, mid);
    build(rson, mid + 1, r);
    up(id);
}

void modify(int id, int l, int r, int ql, int qr, int val)
{
    if ((seg[id].orsum & val) == seg[id].orsum)
        return;
    if (l == r)
    {
        seg[id].orsum &= val;
        seg[id].sum = seg[id].orsum * seg[id].orsum % mod;
        return;
    }
    int mid = l + r >> 1;
    if (qr <= mid)
        modify(lson, l, mid, ql, qr, val);
    else if (ql > mid)
        modify(rson, mid + 1, r, ql, qr, val);
    else
    {
        modify(lson, l, mid, ql, qr, val);
        modify(rson, mid + 1, r, ql, qr, val);
    }
    up(id);
}

int query(int id, int l, int r, int ql, int qr)
{
    if (ql <= l && r <= qr)
    {
        return seg[id].sum;
    }
    int mid = l + r >> 1;
    if (qr <= mid)
        return query(lson, l, mid, ql, qr) % mod;
    else if (ql > mid)
        return query(rson, mid + 1, r, ql, qr) % mod;
    else
        return (query(lson, l, mid, ql, qr) + query(rson, mid + 1, r, ql, qr)) % mod;
}

void solve()
{
    cin >> n;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    build(1, 1, n);
    cin >> q;
    while (q--)
    {
        int op, l, r, x;
        cin >> op;
        if (op == 1)
        {
            cin >> l >> r >> x;
            modify(1, 1, n, l, r, x);
        }
        else if (op == 2)
        {
            cin >> l >> r;
            cout << query(1, 1, n, l, r) << endl;
        }
    }
}
signed main(void)
{
    Zeoy;
    int T = 1;
    // cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}
#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 998244353;
const double eps = 1e-9;
const int N = 3e5 + 10, M = 4e5 + 10;

int n, q;
int a[N];
struct node
{
    int sum;
    int bit[25];
} seg[N << 2];

void up(int id)
{
    seg[id].sum = (seg[lson].sum + seg[rson].sum) % mod;
    for (int i = 0; i <= 24; ++i)
        seg[id].bit[i] = seg[lson].bit[i] + seg[rson].bit[i];
}

void build(int id, int l, int r)
{
    if (l == r)
    {
        seg[id].sum = a[l] * a[l] % mod;
        for (int i = 0; i <= 24; ++i)
        {
            if ((a[l] >> i & 1) == 1)
                seg[id].bit[i] = 1;
            else
                seg[id].bit[i] = 0;
        }
        return;
    }
    int mid = l + r >> 1;
    build(lson, l, mid);
    build(rson, mid + 1, r);
    up(id);
}

void modify(int id, int l, int r, int ql, int qr, int val)
{
    if (l == r)
    {
        int res = 0;
        for (int i = 0; i <= 24; ++i)
        {
            if (seg[id].bit[i] && (val >> i & 1))
                res += (1 << i);
            else
                seg[id].bit[i] = 0;
        }
        seg[id].sum = res * res % mod;
    }
    if (ql <= l && r <= qr)
    {
        bool flag = false;
        for (int i = 0; i <= 24; ++i)
        {
            if (seg[id].bit[i] && (val >> i & 1) == 0)	//如果区间某位为1,但是val为0,说明一定会修改它的叶子节点,直接递归下去即可,否则直接返回
            {
                flag = true;
                break;
            }
        }
        if (!flag)
            return;
    }
    int mid = l + r >> 1;
    if (qr <= mid)
        modify(lson, l, mid, ql, qr, val);
    else if (ql > mid)
        modify(rson, mid + 1, r, ql, qr, val);
    else
    {
        modify(lson, l, mid, ql, qr, val);
        modify(rson, mid + 1, r, ql, qr, val);
    }
    up(id);
}

int query(int id, int l, int r, int ql, int qr)
{
    if (ql <= l && r <= qr)
    {
        return seg[id].sum;
    }
    int mid = l + r >> 1;
    if (qr <= mid)
        return query(lson, l, mid, ql, qr) % mod;
    else if (ql > mid)
        return query(rson, mid + 1, r, ql, qr) % mod;
    else
        return (query(lson, l, mid, ql, qr) + query(rson, mid + 1, r, ql, qr)) % mod;
}

void solve()
{
    cin >> n;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    build(1, 1, n);
    cin >> q;
    while (q--)
    {
        int op, l, r, x;
        cin >> op;
        if (op == 1)
        {
            cin >> l >> r >> x;
            modify(1, 1, n, l, r, x);
        }
        else if (op == 2)
        {
            cin >> l >> r;
            cout << query(1, 1, n, l, r) << endl;
        }
    }
}
signed main(void)
{
    Zeoy;
    int T = 1;
    // cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

Problem K. 子串翻转回文串

给定字符串\(s\),选择其任意子串,然后将该子串翻转,询问能否使得翻转后的字符串\(s\)是回文串

题解:字符串哈希

首先我们知道如果\(s\)两端已经相同的部分不必注意,直接忽略即可,所以我们先找到两端第一个不相同的点\(pos\)\(n-pos+1\),然后翻转的子串要么以\(pos\)为左端点,即\([pos,i]\),要么以\(n-pos+1\)为右端点,即\([i,n-pos+1]\)

所以我们可以直接遍历\([pos,n-pos+1]\),利用字符串哈希判断翻转后是否为回文串即可

我们来说说怎么用字符串哈希判断:假设需要翻转的子串为\([l,r]\)

方法1.我们可以删去 \([l,r]\)处的正向哈希值,然后加上 \([l,r]\)处的反向哈希值,这代表翻转后的正向遍历的哈希值,我们删去 \([l,r]\)处的反向哈希值,然后加上 \([l,r]\)处的正向哈希值,这代表翻转后的反向遍历的哈希值,比较两个哈希值是否相等即可

方法2:翻转\([l,r]\)后的哈希值加上\([r+1,n-pos+1]\)的哈希值得到正向遍历的哈希值,同理得到反向遍历的哈希值,比较两者即可,不再赘述

#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 5e5 + 10, M = 4e5 + 10;

int base = 131;
int h1[N], h2[N], p[N];

int get_hash1(int l, int r)
{
    return (h1[r] - h1[l - 1] * p[r - l + 1] % mod + mod) % mod;
}
int get_hash2(int l, int r)
{
    return (h2[l] - h2[r + 1] * p[r - l + 1] % mod + mod) % mod;
}

bool check(int l, int r, int n)
{
    int sum1 = (h1[n] - get_hash1(l, r) * p[n - r] % mod + get_hash2(l, r) * p[n - r] % mod + mod) % mod;
    int sum2 = (h2[1] - get_hash2(l, r) * p[l - 1] % mod + get_hash1(l, r) * p[l - 1] % mod + mod) % mod;
    return sum1 == sum2;
}

void solve()
{
    string s;
    cin >> s;
    int n = s.length();
    s = " " + s;
    bool flag = true;
    int pos = 0;
    for (int i = 1; i <= n / 2; ++i)
    {
        if (s[i] != s[n - i + 1])
        {
            pos = i;
            flag = false;
            break;
        }
    }
    if (flag)
    {
        cout << "Yes" << endl;
        return;
    }
    h1[0] = 0;
    p[0] = 1;
    for (int i = 1; i <= n; ++i)
    {
        h1[i] = (h1[i - 1] * base % mod + s[i]) % mod;
        p[i] = p[i - 1] * base % mod;
    }
    h2[n + 1] = 0;
    for (int i = n; i >= 1; i--)
        h2[i] = (h2[i + 1] * base % mod + s[i]) % mod;
    flag = false;
    for (int i = pos; i <= n - pos + 1; i++)
    {
        int l1 = pos, r1 = i;
        int l2 = i, r2 = n - pos + 1;
        if (check(l1, r1, n) || check(l2, r2, n))
        {
            flag = true;
            break;
        }
    }
    if (flag)
        cout << "Yes" << endl;
    else
        cout << "No" << endl;
}
signed main(void)
{
    Zeoy;
    int T = 1;
    cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}
#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 5e5 + 10, M = 4e5 + 10;

int base = 131;
int h1[N], h2[N], p[N];

int get_hash1(int l, int r)
{
    return (h1[r] - h1[l - 1] * p[r - l + 1] % mod + mod) % mod;
}
int get_hash2(int l, int r)
{
    return (h2[l] - h2[r + 1] * p[r - l + 1] % mod + mod) % mod;
}

bool check1(int l, int r, int n, int pos)
{
    int sum1 = (get_hash2(l, r) * p[n - pos + 1 - r] % mod + get_hash1(r + 1, n - pos + 1)) % mod;
    int sum2 = (get_hash2(r + 1, n - pos + 1) * p[r - l + 1] % mod + get_hash1(l, r)) % mod;
    return sum1 == sum2;
}

bool check2(int l, int r, int n, int pos)
{
    int sum1 = (get_hash1(l, r) * p[l - pos] % mod + get_hash2(pos, l - 1)) % mod;
    int sum2 = (get_hash1(pos, l - 1) * p[r - l + 1] % mod + get_hash2(l, r)) % mod;
    return sum1 == sum2;
}

void solve()
{
    string s;
    cin >> s;
    int n = s.length();
    s = " " + s;
    bool flag = true;
    int pos = -1;
    for (int i = 1; i <= n / 2; ++i)
    {
        if (s[i] != s[n - i + 1])
        {
            pos = i;
            flag = false;
            break;
        }
    }
    if (flag)
    {
        cout << "YES" << endl;
        return;
    }
    h1[0] = 0;
    p[0] = 1;
    for (int i = 1; i <= n; ++i)
    {
        h1[i] = (h1[i - 1] * base % mod + s[i]) % mod;
        p[i] = p[i - 1] * base % mod;
    }
    h2[n + 1] = 0;
    for (int i = n; i >= 1; i--)
        h2[i] = (h2[i + 1] * base % mod + s[i]) % mod;
    flag = false;
    for (int i = pos; i <= n - pos + 1; i++)
    {
        int l1 = pos, r1 = i;
        int l2 = i, r2 = n - pos + 1;
        if (check1(l1, r1, n, pos) || check2(l2, r2, n, pos))
        {
            flag = true;
            break;
        }
    }
    if (flag)
        cout << "YES" << endl;
    else
        cout << "NO" << endl;
}
signed main(void)
{
    Zeoy;
    int T = 1;
    cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}
posted @ 2023-03-15 11:54  Zeoy_kkk  阅读(238)  评论(1编辑  收藏  举报