杂题小记(2023.02.24)
杂题小记(2023.02.24)
更好的阅读体验戳此进入
LG-P5251 [LnOI2019]第二代图灵机
题面
维护一堆奇奇怪怪的东西。
Solution
考虑线段树维护权值,支持单点修改区间 $ \max $ 和区间 $ \min $,ODT 维护颜色,然后在 ODT 上做双指针并在线段树上查询即可,细节巨多,双指针边界需要精细考虑并实现,还感觉有点类似莫队的思想,好题。
Code
#define _USE_MATH_DEFINES
#include <bits/stdc++.h>
#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW void* Edge::operator new(size_t){static Edge* P = ed; return P++;}
#define ROPNEW_NODE void* Node::operator new(size_t){static Node* P = nd; return P++;}
using namespace std;
mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}
typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;
#define LIM (110000)
#define INF (0x3f3f3f3f)
template < typename T = int >
inline T read(void);
int N, M, C;
int val[LIM], col[LIM];
int cnt[110];
class SegTree{
private:
int sum[LIM << 2], mn[LIM << 2], mx[LIM << 2];
#define LS (p << 1)
#define RS (LS | 1)
#define MID ((gl + gr) >> 1)
public:
SegTree(void){memset(mn, 0x3f, sizeof mn);}
void Pushup(int p){
sum[p] = sum[LS] + sum[RS];
mn[p] = min(mn[LS], mn[RS]);
mx[p] = max(mx[LS], mx[RS]);
}
void Build(int p = 1, int gl = 1, int gr = N){
if(gl == gr)return sum[p] = mn[p] = mx[p] = val[gl = gr], void();
Build(LS, gl, MID), Build(RS, MID + 1, gr);
Pushup(p);
}
void Modify(int pos, int val, int p = 1, int gl = 1, int gr = N){
if(gl == gr)return sum[p] = mn[p] = mx[p] = val, void();
if(pos <= MID)Modify(pos, val, LS, gl, MID);
else Modify(pos, val, RS, MID + 1, gr);
Pushup(p);
}
int QuerySum(int l, int r, int p = 1, int gl = 1, int gr = N){
if(l <= gl && gr <= r)return sum[p];
if(l > gr || r < gl)return 0;
return QuerySum(l, r, LS, gl, MID) + QuerySum(l, r, RS, MID + 1, gr);
}
int QueryMin(int l, int r, int p = 1, int gl = 1, int gr = N){
if(l <= gl && gr <= r)return mn[p];
if(l > gr || r < gl)return INF;
return min(QueryMin(l, r, LS, gl, MID), QueryMin(l, r, RS, MID + 1, gr));
}
int QueryMax(int l, int r, int p = 1, int gl = 1, int gr = N){
if(l <= gl && gr <= r)return mx[p];
if(l > gr || r < gl)return 0;
return max(QueryMax(l, r, LS, gl, MID), QueryMax(l, r, RS, MID + 1, gr));
}
}st;
struct Node{
int l, r;
mutable int val;
friend const bool operator < (const Node &a, const Node &b){
return a.l < b.l;
}
};
class ODT{
private:
set < Node > tr;
public:
auto Insert(Node p){return tr.insert(p);}
auto Split(int p){
auto it = tr.lower_bound(Node{p});
if(it != tr.end() && it->l == p)return it;
advance(it, -1);
if(it->r < p)return tr.end();
auto l = it->l, r = it->r, val = it->val;
tr.erase(it);
Insert(Node{l, p - 1, val});
return Insert(Node{p, r, val}).first;
}
void Assign(int l, int r, int val){
auto itR = Split(r + 1), itL = Split(l);
tr.erase(itL, itR);
Insert(Node{l, r, val});
}
int Query3(int l, int r){
if(C == 1)return st.QueryMin(l, r);
int ans(INT_MAX);
int tot(0);
auto itR = Split(r + 1), itL = Split(l);
auto itl = itL, itr = itL;
while(itl != itR){
while(tot < C && itr != itR)if(!cnt[(itr++)->val]++)++tot;
if(tot == C){
while(cnt[itl->val] > 1)--cnt[(itl++)->val];
ans = min(ans, st.QuerySum(itl->r, prev(itr)->l));
while(tot == C && itl != itR)if(!--cnt[(itl++)->val])--tot;
}
if(itr == itR)while(itl != itR)--cnt[(itl++)->val];
}return ans == INT_MAX ? -1 : ans;
}
int Query4(int l, int r){
int ans = st.QueryMax(l, r);
auto itR = Split(r + 1), itL = Split(l);
auto itl = itL, itr = itL;
while(itl != itR){
while(itr != itR && itr->r - itr->l + 1 == 1 && !cnt[itr->val])cnt[(itr++)->val]++;
if(itr != itR && itr->r - itr->l + 1 > 1){
if(!cnt[itr->val])ans = max(ans, st.QuerySum(itl->r, itr->l));
else if(itl != itr){
ans = max(ans, st.QuerySum(itl->r, prev(itr)->l));
while(cnt[itr->val])cnt[(itl++)->val]--;
ans = max(ans, st.QuerySum(itl->r, itr->l));
}
while(itl != itr)cnt[(itl++)->val]--;
cnt[(itr++)->val]++;
continue;
}
if(itl != itr){
ans = max(ans, st.QuerySum(itl->r, prev(itr)->l));
if(itr == itR)while(itl != itR)cnt[(itl++)->val]--;
else while(itl != itR && cnt[itr->val])cnt[(itl++)->val]--;
}
}return ans;
}
}odt;
int main(){
N = read(), M = read(), C = read();
for(int i = 1; i <= N; ++i)val[i] = read();
for(int i = 1; i <= N; ++i)col[i] = read(), odt.Insert(Node{i, i, col[i]});
st.Build();
while(M--){
int opt = read();
switch(opt){
case 1:{
int p = read(), v = read();
st.Modify(p, v);
break;
}
case 2:{
int l = read(), r = read(), v = read();
odt.Assign(l, r, v);
break;
}
case 3:{
int l = read(), r = read();
printf("%d\n", odt.Query3(l, r));
break;
}
case 4:{
int l = read(), r = read();
printf("%d\n", odt.Query4(l, r));
break;
}
default:break;
}
}
fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
return 0;
}
template < typename T >
inline T read(void){
T ret(0);
int flag(1);
char c = getchar();
while(c != '-' && !isdigit(c))c = getchar();
if(c == '-')flag = -1, c = getchar();
while(isdigit(c)){
ret *= 10;
ret += int(c - '0');
c = getchar();
}
ret *= flag;
return ret;
}
LG-P3765 总统选举
题面
给定 $ n $ 个人的投票,$ m $ 次询问给定 $ l, r, s, k $ 求 $ [l, r] $ 区间严格众数,若不存在则认为为 $ s $,并给定 $ k $ 个数将这些人的投票均改为求出的严格众数,每次输出求得的区间严格众数,特别地,所有操作完成后需要再对 $ [1, n] $ 求一次区间严格众数,不存在则输出 -1
。
Solution
首先摩尔投票默认为前置知识不再提了,不难发现区间众数不支持合并,而摩尔众数形式表示的区间众数则支持,合并是平凡的,则开一颗权值线段树即可。同时考虑验证部分,不难想到对每个人开一个平衡树记录有哪些人对其投票了,这样可以支持动态修改,然后每次查询一下 $ [l, r] $ 内有多少并与总长度比较判断即可。值得一提的是不难发现本题平衡树功能平凡,于是考虑直接使用 tree < int, null_type, less < int >, rb_tree_tag, tree_order_statistics_node_update >
维护即可,可以大幅减少码量以及错误率,但时间上略有降低。
Code
#define _USE_MATH_DEFINES
#include <bits/extc++.h>
#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW void* Edge::operator new(size_t){static Edge* P = ed; return P++;}
#define ROPNEW_NODE void* Node::operator new(size_t){static Node* P = nd; return P++;}
using namespace std;
using namespace __gnu_pbds;
mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}
typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;
template < typename T = int >
inline T read(void);
int N, M;
int vote[510000];
tree < int, null_type, less < int >, rb_tree_tag, tree_order_statistics_node_update > tr[510000];
class SegTree{
private:
int tr[510000 << 2], cnt[510000 << 2];
#define LS (p << 1)
#define RS (LS | 1)
#define MID ((gl + gr) >> 1)
public:
pair < int, int > Merge(pair < int, int > l, pair < int, int > r){
if(l.first == r.first)return {l.first = r.first, l.second + r.second};
if(l.second >= r.second)return {l.first, l.second - r.second};
else return {r.first, r.second - l.second};
}
void Pushup(int p){
tie(tr[p], cnt[p]) = Merge({tr[LS], cnt[LS]}, {tr[RS], cnt[RS]});
}
void Build(int p = 1, int gl = 1, int gr = N){
if(gl == gr)return tr[p] = vote[gl = gr], cnt[p] = 1, void();
Build(LS, gl, MID), Build(RS, MID + 1, gr);
Pushup(p);
}
void Modify(int pos, int p = 1, int gl = 1, int gr = N){
if(gl == gr)return tr[p] = vote[gl = gr], void();
if(pos <= MID)Modify(pos, LS, gl, MID);
else Modify(pos, RS, MID + 1, gr);
Pushup(p);
}
pair < int, int > Query(int l, int r, int p = 1, int gl = 1, int gr = N){
if(l <= gl && gr <= r)return {tr[p], cnt[p]};
pair < int, int > ret{0, -1};
if(l <= MID)ret = Merge(Query(l, r, LS, gl, MID), ret);
if(r >= MID + 1)ret = Merge(Query(l, r, RS, MID + 1, gr), ret);
return ret;
}
}st;
int main(){
N = read(), M = read();
for(int i = 1; i <= N; ++i)tr[vote[i] = read()].insert(i);
st.Build();
while(M--){
int l = read(), r = read(), s = read(), k = read();
auto win = st.Query(l, r).first;
int rnkR = tr[win].order_of_key(r);
if(*tr[win].find_by_order(rnkR) != r)--rnkR;
int rnkL = tr[win].order_of_key(l);
if(rnkR - rnkL + 1 > ((r - l + 1) >> 1))s = win;
while(k--){
int p = read();
tr[vote[p]].erase(p), tr[s].insert(p);
vote[p] = s, st.Modify(p);
}printf("%d\n", s);
}
int l = 1, r = N, s = -1;
auto win = st.Query(l, r).first;
int rnkR = tr[win].order_of_key(r);
if(*tr[win].find_by_order(rnkR) != r)--rnkR;
int rnkL = tr[win].order_of_key(l);
if(rnkR - rnkL + 1 > ((r - l + 1) >> 1))s = win;
printf("%d\n", s);
fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
return 0;
}
template < typename T >
inline T read(void){
T ret(0);
int flag(1);
char c = getchar();
while(c != '-' && !isdigit(c))c = getchar();
if(c == '-')flag = -1, c = getchar();
while(isdigit(c)){
ret *= 10;
ret += int(c - '0');
c = getchar();
}
ret *= flag;
return ret;
}
UPD
update-2023_02_24 初稿