P5251 [LnOI2019]第二代图灵机 题解
很板子的一道题。
思路
由于操作中有关于颜色段相关的操作,又保证了数据随机,所以立马就可以想到 \(\text{ODT}\) ,又由于需要单点修改权值,并查询最大值,最小值,权值和,所以可以想到再用线段树去维护。
是不是十分的简单。
实现细节
这里讲一讲一些实现的细节。
对于第三个和第四个查询操作,由于颜色种类不多,所以我们可以考虑用一个桶将所有的颜色种数存下来。
然后就可以用双指针去维护了。
当然,这里讲一个优化。
我们发现,桶在初始全部赋值为零时,并不需要完全把整个桶全部初始化。
只需要颜色种数多一点就可以了。
掌握了这个优化,轻松拿到 \(\text{rk1}\)(2022.3.5)。
时间复杂度:\(O(nlogn^2)\)。
实际跑得飞快,只跑了 \(933ms\)。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n , m , c , a[N] , b[N] , vis[N];
struct ODT
{
int l , r;
mutable int v;
inline bool operator<(const ODT &tmp) const
{
return l < tmp.l;
}
};
set<ODT> odt;
struct ST
{
int l , r , val , mx , mi;
}t[N * 4];
inline int read()
{
int asd = 0 , qwe = 1; char zxc;
while(!isdigit(zxc = getchar())) if(zxc == '-') qwe = -1;
while(isdigit(zxc)) asd = asd * 10 + zxc - '0' , zxc = getchar();
return asd * qwe;
}
inline void push_up(int p)
{
t[p].val = t[p * 2].val + t[p * 2 + 1].val;
t[p].mx = min(t[p * 2].mx , t[p * 2 + 1].mx);
t[p].mi = max(t[p * 2].mi , t[p * 2 + 1].mi);
}
inline void build(int p , int l , int r)
{
t[p].l = l , t[p].r = r;
if(l == r) { t[p].val = t[p].mx = t[p].mi = a[l]; return; }
build(p * 2 , l , (l + r) / 2);
build(p * 2 + 1 , (l + r) / 2 + 1 , r);
push_up(p);
}
inline void update(int p , int k)
{
if(t[p].l == t[p].r) { t[p].val = t[p].mx = t[p].mi = a[k]; return; }
if((t[p].l + t[p].r) / 2 >= k) update(p * 2 , k);
else update(p * 2 + 1 , k); push_up(p);
}
inline int ask1(int p , int l , int r)
{
if(l <= t[p].l && t[p].r <= r) return t[p].val;
int ans = 0 , mid = (t[p].l + t[p].r) / 2;
if(l <= mid) ans += ask1(p * 2 , l , r); if(r > mid) ans += ask1(p * 2 + 1 , l , r);
return ans;
}
inline int ask2(int p , int l , int r)
{
if(l <= t[p].l && t[p].r <= r) return t[p].mx;
int ans = 1e9 , mid = (t[p].l + t[p].r) / 2;
if(l <= mid) ans = min(ans , ask2(p * 2 , l , r));
if(r > mid) ans = min(ans , ask2(p * 2 + 1 , l , r));
return ans;
}
inline int ask3(int p , int l , int r)
{
if(l <= t[p].l && t[p].r <= r) return t[p].mi;
int ans = 0 , mid = (t[p].l + t[p].r) / 2;
if(l <= mid) ans = max(ans , ask3(p * 2 , l , r));
if(r > mid) ans = max(ans , ask3(p * 2 + 1 , l , r));
return ans;
}
inline auto split(int p)
{
if(p > n) return odt.end();
auto it = --odt.upper_bound((ODT){p , 0 , 0});
if(it->l == p) return it;
int l = it->l , r = it->r , v = it->v;
odt.erase(it) , odt.insert((ODT){l , p - 1 , v});
return odt.insert((ODT){p , r , v}).first;
}
inline void assign(int l , int r , int val)
{
auto it2 = split(r + 1) , it1 = split(l);
odt.erase(it1 , it2) , odt.insert((ODT){l , r , val});
}
inline int query1(int l , int r)
{
memset(vis , 0 , (c+10) * sizeof(int));
auto it2 = split(r + 1) , it1 = split(l);
auto ls = it1 , rs = it1; int res = 1 , ans = 2e9; vis[rs->v] = 1;
while(rs != it2)
{
if(res == c)
{
if(ls == rs) ans = min(ans , ask1(1 , ls->l , ls->r));
else if(ls->r + 1 == rs->l) ans = min(ans , a[ls->r] + a[rs->l]);
else ans = min(ans , a[ls->r] + a[rs->l] + ask1(1 , ls->r + 1 , rs->l - 1));
vis[ls->v]-- , res -= (vis[ls->v] == 0) , ls++;
}
else rs++ , res += (vis[rs->v] == 0) , vis[rs->v]++;
}
return (ans == 2e9 ? -1 : ans);
}
inline bool check(set<ODT>::iterator l , set<ODT>::iterator r)
{
if(l == r || (++l)-- == r) return 0; l++;
for(auto i = l;i != r;i++) if(i->l != i->r) return 1;
return 0;
}
inline int query2(int l , int r)
{
memset(vis , 0 ,(c+10) * sizeof(int));
int ans = ask3(1 , l , r);
auto it2 = split(r + 1) , it1 = split(l) , ls = it1 , rs = it1;
for(;rs != it2;rs++)
{
++vis[rs->v];
while(check(ls , rs)) vis[ls->v]-- , ls++;
while(ls != rs && vis[rs->v] > 1) vis[ls->v]-- , ls++;
if(ls != rs) ans = max(ans , ask1(1 , ls->r , rs->l));
}
return ans;
}
int main()
{
n = read() , m = read() , c = read();
for(int i = 1;i <= n;i++) a[i] = read();
for(int i = 1;i <= n;i++) b[i] = read() , odt.insert((ODT){i , i , b[i]});
build(1 , 1 , n);
for(int i = 1;i <= m;i++)
{
int opt = read();
if(opt == 1)
{
int x = read() , y = read();
a[x] = y , update(1 , x);
}
if(opt == 2)
{
int l = read() , r = read() , x = read();
assign(l , r , x);
}
if(opt == 3)
{
int l = read() , r = read();
printf("%d\n" , query1(l , r));
}
if(opt == 4)
{
int l = read() , r = read();
printf("%d\n" , query2(l , r));
}
}
return 0;
}