2026-02-13~16 hetao1733837 的刷题记录
02-13
LGP4590 [TJOI2018] 游园会
原题链接:[TJOI2018] 游园会
分析
我绝对见过这个题!是不是谁给我推过?
这和 DP 有毛关系?
哈哈,看完 tag 笑了......这是啥?我还是直接看 PPT 吧。
慢慢来......
我们设 L C S x , y LCS_{x,y} LCSx,y 表示 A 串前 x 位,B 串前 y 位的最长公共子序列长度,则
{ L C S x − 1 , y − 1 + 1 , A x = B y max { L C S x − 1 , y , L C S x , y − 1 } , A x ≠ B y \begin{cases} LCS_{x-1,y-1}+1,A_x=B_y \\ \max\{LCS_{x-1,y},LCS_{x,y-1}\},A_x \neq B_y \end{cases} {LCSx−1,y−1+1,Ax=Bymax{LCSx−1,y,LCSx,y−1},Ax=By
然后,我们发现新的串基本是从上一个转过来的,且差为 0/1 ,进行状压,在状态中加判断使不出现 NOI 即可。
正解
cpp
#include <bits/stdc++.h>
#define int long long
#define mod 1000000007
using namespace std;
const int N = 1005, M = 105, O = 35005;
int n, k;
char s[M];
int dp[2][O][3];
int tr1[M], tr2[M], sz[O], ans[M];
int hAsh(int *a){
int res = 0;
for (int i = 0; i < k; i++){
res |= (a[i + 1] - a[i]) << i;
}
return res;
}
void Hash(int *a, int res){
for (int i = 0; i < k; i++){
a[i + 1] = (res >> i) & 1;
}
for (int i = 1; i <= k; i++){
a[i] += a[i - 1];
}
}
void calc(int p1, int p2, int p3, char c, int val){
Hash(tr1, p2);
for (int i = 1; i <= k; i++){
tr2[i] = max({tr2[i - 1], tr1[i], tr1[i - 1] + (c == s[i])});
}
int tp2 = hAsh(tr2);
dp[p1][tp2][p3] = (dp[p1][tp2][p3] + val) % mod;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> k;
cin >> (s + 1);
for (int i = 1; i <= 32767; i++){
sz[i] = sz[i >> 1] + (i & 1);
}
dp[0][0][0] = 1;
for (int i = 0; i < n; i++){
int p1 = (~i) & 1;
int p2 = i & 1;
for (int j = 0; j < (1 << k); j++)
for (int l = 0; l < 3; l++)
dp[p1][j][l] = 0;
for (int j = 0; j < (1 << k); j++){
if (dp[p2][j][0] != 0){
calc(p1, j, 1, 'N', dp[p2][j][0]);
calc(p1, j, 0, 'O', dp[p2][j][0]);
calc(p1, j, 0, 'I', dp[p2][j][0]);
}
if (dp[p2][j][1] != 0){
calc(p1, j, 1, 'N', dp[p2][j][1]);
calc(p1, j, 2, 'O', dp[p2][j][1]);
calc(p1, j, 0, 'I', dp[p2][j][1]);
}
if (dp[p2][j][2] != 0){
calc(p1, j, 1, 'N', dp[p2][j][2]);
calc(p1, j, 0, 'O', dp[p2][j][2]);
}
}
}
for (int i = 0; i < (1 << k); i++){
for (int j = 0; j < 3; j++){
ans[sz[i]] = (ans[sz[i]] + dp[n & 1][i][j]) % mod;
}
}
for (int i = 0; i <= k; i++){
cout << ans[i] << '\n';
}
return 0;
}
02-14
LGP4137 Rmq Problem / mex
原题链接:Rmq Problem / mex
分析
这个东西放到洛谷首页已经几亿年了,居然在今天题单碰到了。
开!呃...... mex \operatorname{mex} mex 有什么性质呢?
不会了,看看题解吧......
权值线段树维护当前值最后一次出现的位置,然后离线一下即可。
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
const int M = N * 40;
int n, m, a[N];
int buc[N << 2], tp;
int rt[N];
int tot;
struct segtree{
struct node{
int ls, rs, val;
}tr[M];
void pushup(int p){
tr[p].val = min(tr[tr[p].ls].val, tr[tr[p].rs].val);
}
void modify(int p, int &pos, int l, int r, int s, int val){
pos = ++tot;
tr[pos].ls = tr[p].ls;
tr[pos].rs = tr[p].rs;
if (l == r){
tr[pos].val = val;
return ;
}
int mid = (l + r) >> 1;
if (s <= mid){
modify(tr[p].ls, tr[pos].ls, l, mid, s, val);
}
else{
modify(tr[p].rs, tr[pos].rs, mid + 1, r, s, val);
}
pushup(pos);
}
int query(int p, int l, int r, int s){
if (!p || l == r){
return buc[l];
}
int mid = (l + r) >> 1;
if (tr[tr[p].ls].val >= s){
return query(tr[p].rs, mid + 1, r, s);
}
else{
return query(tr[p].ls, l, mid, s);
}
}
}T;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
buc[++tp] = 0;
for (int i = 1; i <= n; i++){
cin >> a[i];
buc[++tp] = a[i];
buc[++tp] = a[i] + 1;
}
sort(buc + 1, buc + tp + 1);
tp = unique(buc + 1, buc + tp + 1) - buc - 1;
for (int i = 1; i <= n; i++){
int pos = lower_bound(buc + 1, buc + tp + 1, a[i]) - buc;
T.modify(rt[i - 1], rt[i], 1, tp, pos, i);
}
for (int cs = 1, l, r; cs <= m; cs++){
cin >> l >> r;
cout << T.query(rt[r], 1, tp, l) << '\n';
}
return 0;
}
02-16
LGP9990 [Ynoi Easy Round 2023] TEST_90
原题链接:[Ynoi Easy Round 2023] TEST_90
分析
题面简洁,是我喜欢的题。不是,咋还套了一个计数!值域是 1e6 ,看起来有搞头。
朴素做法显然是......可能是 n 4 n^4 n4,或者 n 5 n^5 n5?懒得算了......从 tag 入手,有一个 扫描线,怎么玩?
很诡异啊......即使某个数出现了偶数次,我也可以......
这个很典啊!维护最后一次出现的位置!
奇偶性搞一个疑惑,维护一下历史版本和,打两个 tag。
正解
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1000005;
int n, a[N], m;
struct node{
int l, r, id;
}inp[N];
int buc[N], lst[N], ans[N];
vector<int> pos[N];
struct segtree{
struct tree{
int len, sum, cnt;
int tag0, tag1, tag;
}tr[N << 2];
void pushup(int p){
tr[p].sum = tr[p << 1].sum + tr[p << 1 | 1].sum;
tr[p].cnt = tr[p << 1].cnt + tr[p << 1 | 1].cnt;
}
void add(int p, int tag0, int tag1){
tr[p].sum += tr[p].cnt * tag1 + (tr[p].len - tr[p].cnt) * tag0;
tr[p].tag0 += tag0;
tr[p].tag1 += tag1;
}
void down(int p){
if (tr[p].tag){
tr[p << 1].tag ^= 1;
swap(tr[p << 1].tag0, tr[p << 1].tag1);
tr[p << 1].cnt = tr[p << 1].len - tr[p << 1].cnt;
tr[p << 1 | 1].tag ^= 1;
swap(tr[p << 1 | 1].tag0, tr[p << 1 | 1].tag1);
tr[p << 1 | 1].cnt = tr[p << 1 | 1].len - tr[p << 1 | 1].cnt;
tr[p].tag = 0;
}
if (tr[p].tag0 || tr[p].tag1){
add(p << 1, tr[p].tag0, tr[p].tag1);
add(p << 1 | 1, tr[p].tag0, tr[p].tag1);
tr[p].tag0 = tr[p].tag1 = 0;
}
}
void build(int p, int l, int r){
tr[p].len = r - l + 1;
if (l == r){
return ;
}
int mid = (l + r) >> 1;
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
}
void modify(int p, int l, int r, int s, int t){
if (s <= l && r <= t){
tr[p].tag ^= 1;
swap(tr[p].tag0, tr[p].tag1);
tr[p].cnt = tr[p].len - tr[p].cnt;
return ;
}
down(p);
int mid = (l + r) >> 1;
if (s <= mid)
modify(p << 1, l, mid, s, t);
if (t > mid)
modify(p << 1 | 1, mid + 1, r, s, t);
pushup(p);
}
int query(int p, int l, int r, int s, int t){
if (s <= l && r <= t){
return tr[p].sum;
}
down(p);
int mid = (l + r) >> 1;
int res = 0;
if (s <= mid)
res += query(p << 1, l, mid, s, t);
if (t > mid)
res += query(p << 1 | 1, mid + 1, r, s, t);
return res;
}
}T;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++){
cin >> a[i];
lst[i] = buc[a[i]];
buc[a[i]] = i;
}
cin >> m;
for (int i = 1; i <= m; i++){
cin >> inp[i].l >> inp[i].r;
inp[i].id = i;
pos[inp[i].r].push_back(i);
}
T.build(1, 1, n);
for (int i = 1; i <= n; i++){
T.modify(1, 1, n, lst[i] + 1, i);
T.add(1, 0, 1);
for (auto v : pos[i]){
ans[inp[v].id] = T.query(1, 1, n, inp[v].l, i);
}
}
for (int i = 1; i <= m; i++){
cout << ans[i] << '\n';
}
}