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}\))
首先引入重要引理:
- \(\lfloor \lfloor n/i \rfloor /j \rfloor = \lfloor n/(i*j) \rfloor\)
- \(\lfloor n/i\rfloor\)的取值只有\(O(\sqrt{n})\)种取值
显然是线性\(dp\),根据题意我们得到:
状态表示:\(f[i][j]\):代表在第\(i\)集人数为\(j\)时能够获得的最大收益
状态属性:\(MAX\)
状态转移:
第\(i\)集投放广告,那么第\(i+1\)集的人数会减少,但会得到收益:\(f[i+1][j/d[i]] = max(f[i+1][j/d[i]],f[i][j]+c*p[i])\)
第\(i\)集不投放广告,那么第\(i+1\)集的人数和收益都不变:
\(f[i+1][j] = max(f[i+1][j],f[i][j])\)
状态优化:
显然二维数组开不下,会\(MLE\),我们利用滚动数组实现,我们发现第\(i\)层由第\(i-1\)层转移得到,且我们发现转移的方向为\(j->j/d[i]\)和\(j->j\),即
那么这就说明我们可以从前往后遍历人数,所以优化后的转移方程为:\(f[j/d[i]] = max(f[j/d[i],f[j]+c*p[i]])\)
- 那么我们解决了空间问题,还需要解决\(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\),支持两种操作:
- 给定\([l,r]\)和\(x\),将区间中的每个元素\(a_i\)修改为各自和\(x\)的与的值,即\(a_i:=a_i\&x\)
- 给定\([l,r]\),询问区间中所有元素的平方和
共有\(q\)次操作,\(a_i<2^{24}\)
题解:在线段树上递归\(O(n·logn+q·loga)\)
我们可以在线段树上维护”区间或“和区间平方和
- 如果一个区间中的区间或&\(x\)后值没有改变,说明该区间中没有必要去修改,也就是说区间中没有元素的值&\(x\)后会发生变化
- 如果一个区间中的区间或&\(x\)后值发生改变,说明一定有元素\(a_i\)需要修改,我们递归到子节点对子节点进行修改
- 我们考虑在线段树上递归的复杂度:首先\(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;
}